【发布时间】:2017-09-07 08:19:03
【问题描述】:
我正在尝试将来自ActionBlock<int> 的一条消息的副本发送给多个同样是ActionBlock<int> 的消费者。这很好用,但是如果目标块之一引发异常,似乎这不会传播到源块。这是我尝试处理异常的方法,但它永远不会进入catch 部分:
static void Main(string[] args)
{
var t1 = new ActionBlock<int>(async i =>
{
await Task.Delay(2000);
Trace.TraceInformation($"target 1 | Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} | message {i}");
}, new ExecutionDataflowBlockOptions { BoundedCapacity = 5 });
var t2 = new ActionBlock<int>(async i =>
{
await Task.Delay(1000);
Trace.TraceInformation($"target 2 | Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} | message {i}");
}, new ExecutionDataflowBlockOptions { BoundedCapacity = 5 });
var t3 = new ActionBlock<int>(async i =>
{
await Task.Delay(100);
Trace.TraceInformation($"target 3 | Thread {System.Threading.Thread.CurrentThread.ManagedThreadId} | message {i}");
if (i > 5)
throw new Exception("Too big number");
}, new ExecutionDataflowBlockOptions { BoundedCapacity = 5 });
var targets = new [] { t1, t2, t3};
var broadcaster = new ActionBlock<int>(
async item =>
{
var processingTasks = targets.Select(async t =>
{
try
{
await t.SendAsync(item);
}
catch
{
Trace.TraceInformation("handled in select"); // never goes here
}
});
try
{
await Task.WhenAll(processingTasks);
}
catch
{
Trace.TraceInformation("handled"); // never goes here
}
});
for (var i = 1; i <= 10; i++)
broadcaster.Post(i);
}
我不确定我在这里遗漏了什么,但我希望能够检索异常以及哪个目标块出现故障。
【问题讨论】:
-
You only
awaitfromTaskfromSendAsync仅表明该项目是否被目标接受。如果任何一个目标引发异常,该异常将附加到该目标的Completion任务。为了观察该异常,您需要await该任务,即await t3.Completion。 -
一个简单的解决方法是用
if (!await t.SendAsync(item)) await t.Completion;替换await t.SendAsync(item);,这会将异常传播到你最内心的try/catch。然后,您可以再次抛出或向新异常添加信息,例如哪个块出现故障。然后你需要处理错误的broadcaster,但你明白了。 -
@JSteward 谢谢!我已经替换为
if (!await t.SendAsync(item)) await t.Completion;,现在一切正常。将其发布为答案,以便我接受。
标签: c# task-parallel-library tpl-dataflow