TPLデータフローと新しい非同期機能を使用するために、古いソケットコードを移植して、TPLデータフローを試しています。APIは堅実に感じられますが、私のコードはそれでも厄介な感じになります。ここで何かが足りないのではないかと思います。
私の要件は次のとおりです。ソケットクラスは、Open、Close、Send、Receiveメソッドを公開します。すべてがタスクを返すため、非同期です。開くと閉じるはアトミックです。送信と受信は互いに隣接して機能しますが、どちらも一度に1つのコマンドしか処理できません。
論理的には、これにより、内部統制の次のコードに進みます。
// exposing an exclusive scheduler for connectivity related tasks and a parallel scheduler where send and receive can work with
private readonly ConcurrentExclusiveSchedulerPair exclusiveConnectionSchedulerPair;
private readonly ActionBlock<Action> connectionBlock;
private readonly ActionBlock<Action> sendBlock;
private readonly ActionBlock<Action> receiveBlock;
// within the constructor:
this.exclusiveConnectionSchedulerPair = new ConcurrentExclusiveSchedulerPair();
this.connectionBlock = new ActionBlock<Action>(action => action(), new ExecutionDataflowBlockOptions() { TaskScheduler = exclusiveConnectionSchedulerPair.ExclusiveScheduler });
this.sendBlock = new ActionBlock<Action>(action => action(), new ExecutionDataflowBlockOptions() { TaskScheduler = exclusiveConnectionSchedulerPair.ConcurrentScheduler });
this.receiveBlock = new ActionBlock<Action>(action => action(), new ExecutionDataflowBlockOptions() { TaskScheduler = exclusiveConnectionSchedulerPair.ConcurrentScheduler });
これまでのところすべて良い。その間に接続関連のアクションが実行されていることを心配することなく、SendandReceiveブロックにアクションを安全に送信できます。また、ActionBlockは、送信する複数の呼び出しが同期されることを保証します(受信、クローズ、およびオープンの理想)。
問題は、アクションがタスクを投稿者に伝える簡単な方法がないことです。現在、TaskCompletionSourceを使用して結果を返しています。好き:
public Task Send(ArraySegment<byte> buffer, CancellationToken cancellationToken)
{
TaskCompletionSource<object> resultCompletionSource = new TaskCompletionSource<object>();
sendBlock.Post(async () =>
{
if (!tcpClient.Connected)
throw new InvalidOperationException("Cant send when not open");
else
{
await sendStream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
resultCompletionSource.SetResult(null);
}
});
return resultCompletionSource.Task;
}
醜くて不器用な感じがします。私の質問は次のとおりです。TPLを使用してワークフローを同期する方法はありますか?その間の通信にTaskCompletionSourceを使用する必要はありませんか?
ありがとう!