Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

И:

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

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

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