2

私は SO の新しいライターです。ご容赦ください。

二重サービス契約を結んだ WCF サービスがあります。このサービス契約には、長いデータ処理を行うことを想定した運用担当者がいます。同時データ処理の数を最大 3 に制限するように制約されています。私の問題は、データ処理後に同じサービス インスタンス コンテキストに戻る必要があるため、イニシエータ エンドポイントをコールバックしてデータ処理結果を渡すことです。さまざまな理由により、TPL データフローと WCF デュプレックスに制約されていることに言及する必要があります。

ここに私がこれまでに書いたもののデモがあります

コンソール ライブラリで、WCF 呼び出しをシミュレートします

class Program
{
    static void Main(string[] args)
    {
        // simulate service calls

        Enumerable.Range(0, 5).ToList().ForEach(x =>
        {
            new System.Threading.Thread(new ThreadStart(async () =>
            {
                var service = new Service();
                await service.Inc(x);
            })).Start();
        });
    }
}

これがWCFサービスであると思われるものです

// service contract
public class Service
{
    static TransformBlock<Message<int>, Message<int>> transformBlock;

    static Service()
    {
        transformBlock = new TransformBlock<Message<int>, Message<int>>(x => Inc(x), new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = 3
        });
    }

    static Message<int> Inc(Message<int> input)
    {
        System.Threading.Thread.Sleep(100);

        return new Message<int> { Token = input.Token, Data = input.Data + 1 };
    }

    // operation contract
    public async Task Inc(int id)
    {
        var token = Guid.NewGuid().ToString();

        transformBlock.Post(new Message<int> { Token = token, Data = id });

        while (await transformBlock.OutputAvailableAsync())
        {
            Message<int> message;
            if (transformBlock.TryReceive(m => m.Token == token, out message))
            {
                // do further processing using initiator service instance members
                // something like Callback.IncResult(m.Data);
                break;
            }
        }
    }
}

public class Message<T>
{
    public string Token { get; set; }

    public T Data { get; set; }
}

オペレーション コントラクトは非同期である必要はありませんが、OutputAvailableAsync 通知が必要でした。

これは良いアプローチですか、それとも私のシナリオに適した解決策はありますか?

前もって感謝します。

4

1 に答える 1

1

まず、トークンをそのように使用するべきではないと思います。一意の識別子は、プロセス間で通信するときに役立ちます。ただし、単一のプロセス内にいる場合は、参照の等価性を使用してください。

実際にあなたの質問に答えるには、(一種の) ビジー ループは良い考えではないと思います。

非同期スロットリングのより簡単なソリューションは、 を使用することSemaphoreSlimです。何かのようなもの:

static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(3);

// operation contract
public async Task Inc(int id)
{
    await Semaphore.WaitAsync();

    try
    {
        Thread.Sleep(100);
        var result = id + 1;
        // do further processing using initiator service instance members
        // something like Callback.IncResult(result);
    }
    finally
    {
        Semaphore.Release();
    }
}

本当にデータフローを使用したい (または使用しなければならない?) 場合はTaskCompletionSource、操作とブロック間の同期に使用できます。操作メソッドは の を待機しTaskTaskCompletionSourceブロックはそのメッセージの計算が終了したときにそれを設定します。

private static readonly ActionBlock<Message<int>> Block =
    new ActionBlock<Message<int>>(
        x => Inc(x),
        new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = 3
        });

static void Inc(Message<int> input)
{
    Thread.Sleep(100);

    input.TCS.SetResult(input.Data + 1);
}

// operation contract
public async Task Inc(int id)
{
    var tcs = new TaskCompletionSource<int>();

    Block.Post(new Message<int> { TCS = tcs, Data = id });

    int result = await tcs.Task;
    // do further processing using initiator service instance members
    // something like Callback.IncResult(result);
}
于 2013-03-21T21:00:48.750 に答える