我正在设计一个由多个 block 组成的长期运行的数据流管道。项目被馈送到管道的输入 block ,最终通过它,并在最后显示在 UI 中(出于对用户的礼貌——管道的真正工作是将处理结果保存到磁盘)。
管道 block 内的 lambda 函数可能会抛出异常,原因有多种(输入错误、网络故障、计算错误等)。在这种情况下,我不想让整个管道出错,而是想踢出有问题的项目,并将其显示在 UI 中的“错误”下。
最好的方法是什么?我知道我可以将每个 lambda 函数包装在一个 try/catch 中:
var errorLoggingBlock = new ActionBlock<Tuple<WorkItem, Exception>>(...)
var workerBlock = new TransformBlock<WorkItem, WorkItem>(item =>
{
try {
return DoStuff(item);
} catch (Exception ex) {
errorLoggingBlock.SendAsync(Tuple.Create(item, ex));
return null;
}
}
但我在管道中有大约 10 个 block ,将代码复制/粘贴到每个 block 中似乎很愚蠢。另外,我不喜欢返回 null 的想法,因为现在所有下游 block 都必须检查它。
我的下一个最佳想法是创建一个返回 lambda 的函数,为我进行包装:
private Func<TArg, TResult> HandleErrors<TArg, TResult>(Func<TArg, TResult> f) where TArg:WorkItem
{
return arg =>
{
try {
return f(arg);
} catch (Exception ex) {
errorLoggingBlock.SendAsync(Tuple.Create(item, ex));
return default(TResult);
}
};
}
但这似乎有点太元了。有没有更好的方法?
最佳答案
这是一个非常有趣的主题。
您可以在链接 block 时定义过滤器,这意味着您可以将错误结果转移到错误处理 block 。为此, block 应该返回包含其处理结果和至少一个失败/成功指示符的“元”对象。
这个想法在 Railroad Oriented Programming 中有更好的描述。 ,其中链中的每个函数处理成功的结果或将失败的结果转移到“失败的轨道”以进行最终记录。
实际上,这意味着您应该在每个 block 之后添加两个链接:一个具有转移到错误处理 block 的过滤条件,以及一个转到流程中下一步的默认链接。
您甚至可以结合这两种想法来处理部分故障。部分失败结果将包含失败指示符和有效负载。在将结果传递到下一步之前,您可以将结果转移到日志记录 block 。
我发现明确每条消息的状态比尝试通过检查空值、缺失值等来确定其状态要容易多。这意味着 block 应该包装它们的结果在包含状态标志、结果和/或任何错误的“信封”对象中。
关于c# - 数据流管道中的全局每 block 错误处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31956521/