如何包含任务参数在任务数组中进行异常处理

时间:2017-12-29 07:27:52

标签: .net exception-handling parallel-processing async-await

线程Nesting await in Parallel.ForEach有一个答案,建议使用Task.WhenAll并行运行多个(MaxDegreeOfParallelism)异步任务,而不是等到上一个任务完成。

public static Task ForEachAsync<T>(
      this IEnumerable<T> source, int dop, Func<T, Task> body) 
{ 
    return Task.WhenAll( 
        from partition in Partitioner.Create(source).GetPartitions(dop) 
        select Task.Run(async delegate { 
            using (partition) 
                while (partition.MoveNext()) 
                    await body(partition.Current).ContinueWith(t => 
                          {
                              //observe exceptions
                          });
})); 
}

并称之为

ids.ForEachAsync(10,  async id =>
{
    ICustomerRepo repo = new CustomerRepo();  
    var cust = await repo.GetCustomer(id);  
    customers.Add(cust);  
});

如果body有参数,我想在处理例外时知道参数值。如果任务主体的id失败,我需要记录异常,指明它是否发生了特定的id。

我看过了 Accessing values in Task.ContinueWith,但在t.IsFaulted时无法访问参数。

最后我在lambda体内添加了try / catch,它似乎正常工作

ids.ForEachAsync(10,  async id =>
{
    try
    {
        ICustomerRepo repo = new CustomerRepo();
        var cust = await repo.GetCustomer(id);
        customers.Add(cust);
    }
    catch(Exception e)
    {
        _logger.LogError(e,” id=“+ id);
    }
});

但是我不确定,它是否正常工作(即异步,不阻塞)。

后来原始答案suggested的作者在await body之前使用var current = partition.Current然后在continuation中使用current(ContinueWith(t =&gt; {...})。 -

任何人都可以确认,哪种方法更好?每种方法的任何缺点?

1 个答案:

答案 0 :(得分:3)

await / try中包裹catch很好,请参阅:Catch an exception thrown by an async method。与我的建议没有太大区别(捕获partition.Current并注入ContinueWith延续),除非它更有效,因为不涉及捕获。此外,我认为它更具可读性和优雅性,ContinueWith是一种旧的&#34;做事方式(前async / await)。

请注意,您的示例将异常处理的负担传递给调用者(在本例中调用_logger.LogError)。你需要确保你想要的是什么,而不是ForEachAsync代码本身中嵌入的全部内容来处理调用者确实让异常漏掉的情况。类似的东西:

while (partition.MoveNext()) 
{
    try
    {
        await body(partition.Current)
    }
    catch (Exception e)
    {
        // of course here you don't know the type of T (partition.Current)
        // or anything else about the operation for that matter
        LogError("error processing: " + partition.Current + ": " + e); 
    }
}