System.Threading.Tasks - 无法使用Task.WaitAll()捕获异常

时间:2014-03-26 22:48:53

标签: c#

我尝试并行运行某些任务,每个任务都返回一个值。一旦完成,我想利用那些返回的结果。我遇到的问题是如果其中一个任务抛出一个异常就捕获异常。

根据我在其他一些SO问题上看到的内容,我认为下面的简化代码应该可行。当我在visual studio中运行它时,我的异常无法处理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;

class Program
    {
        static void Main()
        {
            int[] ids = { 1, 2, 3, 4 };
            Test t = new Test();
             t.Process(ids);
        }
    }

    class Test
    {
        public void Process(int[] ids)
        {
            var tasks = new List<Task<Tuple<int, bool>>>(ids.Count());
            foreach (int id in ids)
            {
                //The task will run at an indeterminate time so create a copy of id as it may have a new value assigned before that occurs 
                int i = id;
                var task = Task.Factory.StartNew(() => DoWork(i));
                tasks.Add(task);
            }
            try
            {
                Task.WaitAll(tasks.ToArray());
            }
            catch (AggregateException ex)
            {
                foreach (var innerEx in ex.InnerExceptions)
                {
                    Console.WriteLine(innerEx);
                }

            }
            foreach (var t in tasks)
            {
                Tuple<int, bool> result = t.Result;

                Console.WriteLine(string.Format("{0} - success = {1}", result.Item1, result.Item2));
            }
        }

        private Tuple<int, bool> DoWork(int i)
        {

            Console.WriteLine(string.Format("Processing {0}", i));
            Thread.Sleep(i * 500);
            if (i == 3)
            {
                //This exception does not get caught.
                throw new Exception(string.Format("exception thrown on: {0}", i));
            }
            return new Tuple<int, bool>(i, true);
        }
    }

我期待的应该打印(由于并行运行,订单可能会改变):

处理1
处理2
处理3
处理4
例外:3
1 - 成功=真实 2 - 成功=真实 4 - 成功=真实

我是否完全误解了Task.WaitAll的工作方式?

1 个答案:

答案 0 :(得分:1)

不,你不是。但是,您正在观察异常两次。 Task.WaitAll将抛出异常,您的代码将输出异常信息。然后,当您从t.Result获取该任务的结果时,您将再次获得异常(任务仍然包含引发异常的任务)。

您至少有两种选择来解决此问题:

  1. 从列表中删除生成异常的任务 任务。
  2. 删除你的Task.WaitAll并直接浏览你的foreach 任务期望t.Result将阻止该任务 完成(如果该任务抛出异常,则抛出异常)。
  3. 这里是#2的更新:

    public void Process(int[] ids)
    {
        var tasks = new List<Task<Tuple<int, bool>>>(ids.Count());
        foreach (int id in ids)
        {
            //The task will run at an indeterminate time so create a copy of id as it may have a new value assigned before that occurs 
            int i = id;
            var task = Task.Factory.StartNew(() => DoWork(i));
            tasks.Add(task);
        }
        foreach (var t in tasks)
        {
            try
            {
                Tuple<int, bool> result = t.Result;
    
                Console.WriteLine(string.Format("{0} - success = {1}", result.Item1, result.Item2));
            }
            catch (AggregateException ex)
            {
                foreach (var innerEx in ex.InnerExceptions)
                {
                    Console.WriteLine(innerEx.Message);
                }
            }
        }
    }