我需要某种类似于BroadcastBlock的对象,但保证交付。所以我使用了this question的答案。但我真的不清楚这里的执行流程。我有一个控制台应用程序。这是我的代码:
static void Main(string[] args)
{
ExecutionDataflowBlockOptions execopt = new ExecutionDataflowBlockOptions { BoundedCapacity = 5 };
List<ActionBlock<int>> blocks = new List<ActionBlock<int>>();
for (int i = 0; i <= 10; i++)
blocks.Add(new ActionBlock<int>(num =>
{
int coef = i;
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ". " + num * coef);
}, execopt));
ActionBlock<int> broadcaster = new ActionBlock<int>(async num =>
{
foreach (ActionBlock<int> block in blocks) await block.SendAsync(num);
}, execopt);
broadcaster.Completion.ContinueWith(task =>
{
foreach (ActionBlock<int> block in blocks) block.Complete();
});
Task producer = Produce(broadcaster);
List<Task> ToWait = new List<Task>();
foreach (ActionBlock<int> block in blocks) ToWait.Add(block.Completion);
ToWait.Add(producer);
Task.WaitAll(ToWait.ToArray());
Console.ReadLine();
}
static async Task Produce(ActionBlock<int> broadcaster)
{
for (int i = 0; i <= 15; i++) await broadcaster.SendAsync(i);
broadcaster.Complete();
}
每个数字必须按顺序处理,所以我不能在广播块中使用MaxDegreeOfParallelism。但是,接收该号码的所有操作块都可以并行运行。
所以这就是问题:
在输出中我可以看到不同的线程ID。我是否理解正确如下:
执行人员在广播公司中点击await block.SendAsync(num);
。
如果当前块未准备好接受该号码,则执行退出广播器并挂起在Task.WaitAll。
当块接受该号码时,广播器中的其余foreach语句将在线程池中执行。
直到最后。
foreach的每次迭代都在线程池中执行。但实际上它是按顺序发生的。
我的理解是对还是错? 如何更改此代码以异步方式将数字发送到所有块?
为了确保如果其中一个区块此时尚未准备好接收该号码,我将不会等待它,所有其他已准备好的区块将收到该号码。并且所有块都可以并行运行。并保证交付。
答案 0 :(得分:5)
假设您希望broadcaster
一次处理一个项目,同时启用目标块同时接收该项目,您需要更改broadcaster
以同时为所有块提供该数字时间,然后异步等待所有一起接受它,然后再转到下一个数字:
var broadcaster = new ActionBlock<int>(async num =>
{
var tasks = new List<Task>();
foreach (var block in blocks)
{
tasks.Add(block.SendAsync(num));
}
await Task.WhenAll(tasks);
}, execopt);
现在,在这种情况下,在等待之后你没有工作,你可以稍微优化,同时仍然返回一个等待的任务:
ActionBlock<int> broadcaster = new ActionBlock<int>(
num => Task.WhenAll(blocks.Select(block => block.SendAsync(num))), execopt);