web-dev-qa-db-ja.com

TPL Dataflow、Post()とSendAsync()の機能的な違いは何ですか?

Post()またはSendAsync()によるアイテムの送信の違いについて混乱しています。私の理解では、すべての場合において、アイテムがデータブロックの入力バッファーに到達すると、制御は呼び出しコンテキストに戻ります。では、なぜSendAsyncが必要になるのでしょうか。私の仮定が正しくない場合は、反対に、データブロックを使用するという考え全体が同時の非同期環境を確立することであるのに、なぜPost()を使用するのでしょうか。

もちろん、私はPost()がブール値を返すのに対し、SendAsyncは待機可能なブール値のタスクを返すという点で技術的に違いを理解しています。しかし、それはどんな意味を持っていますか? bool(アイテムがデータブロックのキューに配置されたかどうかの確認です)の返送が遅れるのはいつですか?私は非同期/待機同時実行フレームワークの一般的な考え方を理解していますが、ここではあまり意味がありません。渡されたアイテムに対して行われた結果のブール値以外は、呼び出し元に返されることはなく、代わりに「アウトキュー」になり、リンクされたデータブロックに転送されるか、破棄されます。

また、アイテムを送信する場合、2つの方法の間にパフォーマンスの違いはありますか?

48
Matthias Wolf

違いを確認するには、ブロックがメッセージを延期する状況が必要です。この場合、Postfalseをすぐに返しますが、SendAsyncTaskを返します。これは、ブロックがメッセージの処理方法を決定したときに完了します。 。 Taskは、メッセージが受け入れられる場合はtrue結果を受け取り、受け入れられない場合はfalse結果を受け取ります。

延期の状況の一例は、貪欲でない参加です。より簡単な例は、BoundedCapacityを設定する場合です。

[TestMethod]
public void Post_WhenNotFull_ReturnsTrue()
{
    var block = new BufferBlock<int>(new DataflowBlockOptions {BoundedCapacity = 1});

    var result = block.Post(13);

    Assert.IsTrue(result);
}

[TestMethod]
public void Post_WhenFull_ReturnsFalse()
{
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 });
    block.Post(13);

    var result = block.Post(13);

    Assert.IsFalse(result);
}

[TestMethod]
public void SendAsync_WhenNotFull_ReturnsCompleteTask()
{
    // This is an implementation detail; technically, SendAsync could return a task that would complete "quickly" instead of already being completed.
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 });

    var result = block.SendAsync(13);

    Assert.IsTrue(result.IsCompleted);
}

[TestMethod]
public void SendAsync_WhenFull_ReturnsIncompleteTask()
{
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 });
    block.Post(13);

    var result = block.SendAsync(13);

    Assert.IsFalse(result.IsCompleted);
}

[TestMethod]
public async Task SendAsync_BecomesNotFull_CompletesTaskWithTrueResult()
{
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 });
    block.Post(13);
    var task = block.SendAsync(13);

    block.Receive();

    var result = await task;
    Assert.IsTrue(result);
}

[TestMethod]
public async Task SendAsync_BecomesDecliningPermanently_CompletesTaskWithFalseResult()
{
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 });
    block.Post(13);
    var task = block.SendAsync(13);

    block.Complete();

    var result = await task;
    Assert.IsFalse(result);
}
55
Stephen Cleary

ドキュメントにより、IMOはこれをかなり明確にします。特に、 Post の場合:

このメソッドは、ターゲットブロックがアイテムを受け入れるか拒否するかを決定すると戻りますが、ターゲットブロックの特別なセマンティクスによって別段の指示がない限り、アイテムが実際に処理されるのを待ちません。

そして:

提供されたメッセージの延期をサポートするターゲットブロック、またはPostの実装でより多くの処理を実行する可能性のあるブロックの場合、SendAsyncを使用することを検討してください。後でSendAsyncが戻った後にそれを消費します。

言い換えると、両方とも処理メッセージに関して非同期ですが、SendAsyncを使用すると、ターゲットブロックで承諾メッセージを非同期にするかどうかを決定できます。 。

SendAsyncは一般に「より非同期な」アプローチであり、おそらく一般的に推奨されるアプローチのようです。 ではないはっきりしているのは、両方が必要な理由です。確かに、PostSendAsyncを使用して結果を待つのとほぼ同じように聞こえるためです。コメントで述べたように、isには大きな違いが1つあります。バッファがいっぱいの場合、Postはすぐに拒否しますが、SendAsyncは拒否しません。

14
Jon Skeet