在Task中的Parallel.Invoke中的异常处理

时间:2013-11-05 16:22:45

标签: c# task-parallel-library

我有以下代码

var exceptions = new ConcurrentQueue<Exception>();
Task task = Task.Factory.StartNew(() =>
{
    try
    {
        Parallel.Invoke(
            async () => await _aViewModel.LoadData(_someId),
            async () => await _bViewModel.LoadData(_someId)
        );
    }
    catch (Exception ex)
    {
        exceptions.Enqueue(ex);
    }
}).ContinueWith((continuation) =>
    {
        if (exceptions.Count > 0) throw new AggregateException(exceptions);
    });

我在这里使用Task.StartNew,因为LoadData方法使用Dispatcher.StartAsync方法在内部主UI线程上调用。

我遇到的问题是,如果我强制_aViewModel.LoadData抛出异常,它就不会被Catch(Exception)子句捕获(也不会捕获AggregateException)。我不明白为什么!?

2 个答案:

答案 0 :(得分:4)

Parallel.Invoke不是async - 意识到的。所以你的async lambdas被转换为async void方法,这些方法具有极其笨拙的错误语义(不允许它们离开async void方法;相反,它们被捕获并直接重新提升在SynchronizationContext方法启动时处于活动状态的async void上 - 在本例中为线程池)。

我不确定你为什么首先拥有Parallel.Invoke。由于您的方法已经async,您可以执行以下操作:

Task task = Task.Factory.StartNew(async () =>
{
    try
    {
        Task.WaitAll(
            _aViewModel.LoadData(_someId),
            _bViewModel.LoadData(_someId)
        );
    }
    catch (Exception ex)
    {
        exceptions.Enqueue(ex);
    }
})...

P.S。如果你有时间,重新考虑整个代码部分的结构。 Dispatcher.StartAsync是代码气味。 UI应该(异步地)请求数据;数据检索对象不应该知道UI。

答案 1 :(得分:3)

Parallel.Invoke需要一组Action个代表。它无法知道您的委托实际上是async方法,因此它会在您的任务完成之前返回。

要对此行为进行深入说明,请在主题上观看Lucian Wischik's Channel 9 video

尝试更改代码以改为使用Task.WhenAll method

var aTask = _aViewModel.LoadData(_someId);
var bTask = _bViewModel.LoadData(_someId);
await Task.WhenAll(aTask, bTask);