这个问题涉及使用ContinueWith()处理TPL数据库完成时的最佳实践。
ITargetBlock.Completion()方法允许您使用ContinueWith()异步处理数据库的完成。
考虑下面的控制台应用程序代码,它演示了一个非常基本的用法:
private static void Main()
{
test().Wait();
}
static async Task test()
{
var transform = new TransformBlock<int, double>(i => i/2.0);
var output = new ActionBlock<double>(d => Console.WriteLine(d));
// Warning CS4014 here:
transform.Completion.ContinueWith(continuation => output.Complete());
transform.LinkTo(output);
for (int i = 0; i < 10; ++i)
await transform.SendAsync(i);
transform.Complete();
await output.Completion;
}该代码有一个非常简单的TransformBlock,它将整数除以2.0,并将它们转化为双倍。转换后的数据由ActionBlock处理,它只将值输出到控制台窗口。
产出如下:
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5当TransformBlock完成时,我也要完成ActionBlock。这样做很方便:
transform.Completion.ContinueWith(continuation => output.Complete());问题就在这里。因为这是在一个async方法中,而且我忽略了来自ContinueWith()的返回值,所以我得到了一个编译器警告:
警告CS4014:由于没有等待此调用,因此在调用完成之前,将继续执行当前方法。考虑将“等待”运算符应用于调用的结果.
显然,我不能按照警告的建议进行await调用--如果这样做,代码会挂起,因为在调用transform.Complete()之前,CompleteWith()任务无法完成。
现在,我在处理警告本身方面没有问题(我只需要将任务取消,或者将任务赋值给变量,或者使用.Forget()任务扩展等等)--但下面是我的问题:
我认为,如果我在延续中做的不是output.Complete(),我就必须提供异常处理--但也许仅仅调用output.Complete()也充满了危险?
发布于 2014-12-09 15:31:04
虽然您可以自己实现完成传播,但是TPL使用DataflowLinkOptions.PropagateCompletion为您做了它。
transform.LinkTo(output, new DataflowLinkOptions {PropagateCompletion = true});如果您仍然希望自己实现该任务,则可以解决这个问题,即存储任务,并在适当时使用Task.WhenAll等待它。
static async Task test()
{
var transform = new TransformBlock<int, double>(i => i/2.0);
var output = new ActionBlock<double>(d => Console.WriteLine(d));
// Warning CS4014 here:
var continuation = transform.Completion.ContinueWith(_ => output.Complete());
transform.LinkTo(output);
for (int i = 0; i < 10; ++i)
await transform.SendAsync(i);
transform.Complete();
await Task.WhenAll(continuation, output.Completion)
}这使您能够处理output.Complete中的任何异常并删除警告。
https://stackoverflow.com/questions/27381582
复制相似问题