【问题标题】:Task.WaitAll and ExceptionsTask.WaitAll 和异常
【发布时间】:2011-05-12 04:26:27
【问题描述】:

我对异常处理和并行任务有疑问。

下面显示的代码启动 2 个任务并等待它们完成。我的问题是,如果任务抛出异常,则永远无法到达 catch 处理程序。

        List<Task> tasks = new List<Task>();
        try
        {                
            tasks.Add(Task.Factory.StartNew(TaskMethod1));
            tasks.Add(Task.Factory.StartNew(TaskMethod2));

            var arr = tasks.ToArray();                
            Task.WaitAll(arr);
        }
        catch (AggregateException e)
        {
            // do something
        }

但是,当我使用以下代码等待超时的任务时,会捕获到异常。

 while(!Task.WaitAll(arr,100));

我似乎遗漏了一些东西,因为WaitAll 的文档描述了我第一次尝试成为正确的人。请帮助我理解为什么它不起作用。

【问题讨论】:

  • TaskMethod1 和 TaskMethod2 是做什么的?你在哪个线程上执行?如果你能把它变成一个简短但完整的例子(就像我的回答一样),那真的很有帮助。

标签: c# .net parallel-processing


【解决方案1】:

无法重现 - 它对我来说很好:

using System;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    static void Main()
    {
        Task t1 = Task.Factory.StartNew(() => Thread.Sleep(1000));
        Task t2 = Task.Factory.StartNew(() => {
            Thread.Sleep(500);
            throw new Exception("Oops");
        });

        try
        {
            Task.WaitAll(t1, t2);
            Console.WriteLine("All done");
        }
        catch (AggregateException)
        {
            Console.WriteLine("Something went wrong");
        }
    }
}

正如我所期望的那样打印“出了点问题”。

您的一项任务可能没有完成吗? WaitAll 确实会等待所有任务完成,即使有些任务已经失败。

【讨论】:

  • 感谢乔恩的快速回答!我的问题是/是另一个任务取决于失败的任务,因此它将永远等待失败的任务。我的想法是,当任务失败时会立即捕获异常,但事实并非如此。感谢您指出这一点。
  • @pivotnig - 在此处查看“创建任务延续”以明确表达依赖关系 - msdn.microsoft.com/en-us/library/dd537609.aspx
  • @thumbmunkeys 没有意识到这个帖子。这正是我在这里问的:stackoverflow.com/q/47820918/695964
【解决方案2】:

这就是我解决问题的方法,正如我的回答/问题(上图)的 cmets 中提到的那样:

调用者捕获由屏障协调的任务引发的任何异常,并向其他任务发出强制取消的信号:

CancellationTokenSource cancelSignal = new CancellationTokenSource();
try
{
    // do work
    List<Task> workerTasks = new List<Task>();
    foreach (Worker w in someArray)
    {
        workerTasks.Add(w.DoAsyncWork(cancelSignal.Token);
    }
    while (!Task.WaitAll(workerTasks.ToArray(), 100, cancelSignal.Token)) ;

 }
 catch (Exception)
 {
     cancelSignal.Cancel();
     throw;
 }

【讨论】:

  • 这很好。从未意识到CancellationTokenSource 的这个用例。
【解决方案3】:

我试图为集合中的每个项目创建一个调用,结果如下:

var parent = Task.Factory.StartNew(() => {
  foreach (var acct in AccountList)
    {
      var currAcctNo = acct.Number;
      Task.Factory.StartNew(() =>
      {
        MyLocalList.AddRange(ProcessThisAccount(currAcctNo));
      }, TaskCreationOptions.AttachedToParent);
      Thread.Sleep(50);
    }
  });

我必须在每次添加子任务后添加 Thread.Sleep,因为如果我不这样做,该进程将倾向于在下一次迭代中覆盖 currAcctNo。我的列表中有 3 或 4 个不同的帐号,当它处理每个帐号时,ProcessThisAccount 调用将显示所有调用的最后一个帐号。一旦我进入睡眠,这个过程就很好。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-29
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
相关资源
最近更新 更多