2

我有以下代码

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)。我不明白为什么!?

4

2 回答 2

4

Parallel.Invoke不知道async。因此,您的asynclambda 被转换为async void具有极其尴尬的错误语义的方法(它们不允许离开方法;相反,它们被捕获并直接在方法启动时处于活动状态的方法async void上重新引发- 在这种情况下,线程池)。SynchronizationContextasync 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);
    }
})...

PS如果你有时间,重新考虑这部分代码的结构。Dispatcher.StartAsync是代码气味。UI 应该(异步)请求数据;数据检索对象不必了解 UI。

于 2013-11-05T18:32:06.640 回答
3

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

有关此行为的深入解释,请观看Lucian Wischik关于该主题的第 9 频道视频。

尝试更改代码以改用Task.WhenAll 方法

var aTask = _aViewModel.LoadData(_someId);
var bTask = _bViewModel.LoadData(_someId);
await Task.WhenAll(aTask, bTask);
于 2013-11-05T18:30:59.300 回答