c# concurrency task-parallel-library message-passing tpl-dataflow

c# - Flujo de datos TPL, ¿cuál es la diferencia funcional entre Post() y SendAsync()?



concurrency task-parallel-library (2)

Estoy confundido acerca de la diferencia entre enviar elementos a través de Post () o SendAsync (). Mi entendimiento es que en todos los casos, una vez que un elemento alcanzó el búfer de entrada de un bloque de datos, el control se devuelve al contexto de llamada, ¿correcto? Entonces, ¿por qué necesitaría SendAsync? Si mi suposición es incorrecta, me pregunto, por el contrario, por qué alguien usaría Post () si la idea general de usar bloques de datos es establecer un entorno concurrente y asíncrono.

Por supuesto, entiendo la diferencia técnica en que Post () devuelve un bool mientras que SendAsync devuelve una Tarea de bool que se puede esperar. ¿Pero qué implicaciones tiene eso? ¿Cuándo la devolución de un bool (que entiendo es una confirmación de si el artículo se colocó en la cola del bloque de datos o no) alguna vez se retrasó? Entiendo la idea general del marco de concurrencia asíncrono / espera, pero aquí no tiene mucho sentido porque, aparte de un bool, los resultados de lo que se haga al elemento pasado nunca se devuelven al llamante, sino que se colocan en un "fuera de la cola" y se reenvía a los bloques de datos vinculados o se descarta.

¿Y hay alguna diferencia de rendimiento entre los dos métodos al enviar artículos?


La documentación hace esto razonablemente claro, IMO. En particular, para el Post :

Este método se devolverá una vez que el bloque de destino haya decidido aceptar o rechazar el elemento, pero, a menos que lo indique la semántica especial del bloque de destino, no espera a que el elemento se procese realmente.

Y:

Para los bloques de destino que admiten posponer los mensajes ofrecidos, o para los bloques que pueden hacer más procesamiento en su implementación Post , considere usar SendAsync , que devolverá de inmediato y permitirá que el destino posponga el mensaje publicado y luego lo consuma después de que devuelva SendAsync .

En otras palabras, mientras que ambos son asíncronos con respecto al procesamiento del mensaje, SendAsync permite que el bloque de destino decida si acepta o no el mensaje de forma asíncrona.

Parece que SendAsync es un enfoque generalmente "más asíncrono", y que probablemente se recomienda en general. Lo que no me queda claro es por qué se requieren ambos, ya que ciertamente suena como que Post es ampliamente equivalente a usar SendAsync y luego solo esperar el resultado.


Para ver la diferencia, necesita una situación en la que los bloques pospongan sus mensajes. En este caso, Post devolverá false inmediatamente, mientras que SendAsync devolverá una Task que se completará cuando el bloque decida qué hacer con el mensaje. La Task tendrá un resultado true si el mensaje es aceptado, y un resultado false si no lo es.

Un ejemplo de una situación de aplazamiento es una unión no codiciosa. Un ejemplo más simple es cuando establece 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); }