Поток данных TPL, в чем функциональная разница между Post() и SendAsync()?

Меня смущает разница между отправкой элементов через Post() или SendAsync(). Насколько я понимаю, во всех случаях, когда элемент достигает входного буфера блока данных, управление возвращается в контекст вызова, правильно? Тогда зачем мне нужен SendAsync? Если мое предположение неверно, то мне, наоборот, интересно, почему кто-то когда-либо использовал Post(), если вся идея использования блоков данных заключается в создании параллельной и асинхронной среды.

Конечно, технически я понимаю разницу в том, что Post() возвращает bool, тогда как SendAsync возвращает ожидаемую задачу bool. Но какое это имеет значение? Когда будет когда-либо задерживаться возврат bool (который, как я понимаю, является подтверждением того, был ли элемент помещен в очередь блока данных)? Я понимаю общую идею структуры параллелизма async / await, но здесь это не имеет особого смысла, потому что кроме bool результаты того, что делается с переданным элементом, никогда не возвращаются вызывающей стороне, а вместо этого помещаются в "out-queue" и либо перенаправляется в связанные блоки данных, либо отбрасывается.

И есть ли разница в производительности между двумя методами при отправке товаров?

2 ответа

Решение

Чтобы увидеть разницу, вам нужна ситуация, когда блоки будут откладывать свои сообщения. В этом случае, Post вернусь false немедленно, тогда как SendAsync вернет Task это будет завершено, когда блок решит, что делать с сообщением. 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);
}

Документация делает это достаточно ясно, ИМО. В частности, для Post:

Этот метод вернется, как только целевой блок решит принять или отклонить элемент, но, если иное не определено специальной семантикой целевого блока, он не ожидает фактической обработки элемента.

А также:

Для целевых блоков, которые поддерживают откладывание предлагаемых сообщений, или для блоков, которые могут выполнять больше обработки в своих Post реализация, рассмотреть возможность использования SendAsync, который немедленно вернется и позволит цели отложить отправленное сообщение и позже использовать его после SendAsync возвращается.

Другими словами, в то время как оба являются асинхронными в отношении обработки сообщения, SendAsync позволяет целевому блоку решать, принимать или нет сообщение асинхронно.

Это звучит как SendAsync это, как правило, "более асинхронный" подход, который, вероятно, рекомендуется в целом. Что мне непонятно, так это то, почему оба требуются Post в целом эквивалентно использованию SendAsync а потом просто жду результата.

Другие вопросы по тегам