异常处理:线程与任务的区别

5

线程版本会导致未处理的异常,从而导致应用程序崩溃,但任务版本不会。两者都运行完全相同的方法。有人能解释一下这种异常行为差异的原因吗?

线程版本:

            try
            {
                new Thread(new ThreadStart(DoWork)).Start();  // do work throws exception
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

        static void DoWork()
        {
            Console.WriteLine("in thread");
            throw new Exception();
        }

任务版本:

      var errorTask = Task.Factory.StartNew<Func<string>>(() =>
                {
                    Console.WriteLine("in task");
                    throw new Exception();
                });

            try
            {
                string result = errorTask.Result();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

因为任务(Tasks)不等于线程(Threads)。 - ProgrammingLlama
@john 是的,但对于新手来说,这种行为非常相似,因此可以想象 xe 可能会对异常处理方式的不同感到烦恼。 - Paul Kertscher
2个回答

3

Thread.Start 方法用于启动新线程,但如果您在另一个线程中处理异常:

try
{
    // DoWork throws exception in new thread;
    // threads don't catch exceptions out-of-the-box
    new Thread(new ThreadStart(DoWork)).Start();
}
catch (Exception e)
{
    // you're handling exception in "old" thread
    Console.WriteLine(e);
}

Task.Factory.StartNew 启动一个新的 任务。任务在内部捕获异常,以设置其 Status 属性:

var errorTask = Task.Factory.StartNew<Func<string>>(() =>
{
    Console.WriteLine("in task");

    // this exception will be caught in Task's base code,
    // since tasks catch exceptions thrown by task methods;
    // note, that this will be wrapped into AggregateException
    throw new Exception();
});

当您尝试获取Task.Result,而任务处于故障状态时,它将重新抛出异常:

// this will re-throw exception in calling thread
string result = errorTask.Result;

这就是为什么你的第二个catch会捕获它的原因。

1
为了更好地了解这个主题,可以查阅 Task.Result<TResult>()文档(或者 Task.Wait() 的文档,如果有必要的话)。
在抛出异常(特别是 AggregateException)时,它说:

任务执行过程中抛出了异常。 AggregateException.InnerExceptions 集合包含有关异常的信息。

Task 是一种类似于管理线程的东西(简单来说),它给我们带来了一些优点,例如:当访问 ResultWait(或使用 await)时可以进行异常处理。另一方面,Thread 将在调用它的方法之外单独执行。您启动线程并(虚拟地)立即离开 try / catch 块。对于线程来说,没有办法知道有关联的 try / catch。基本上,线程不知道任何关于调用函数的信息。反过来,如果调用函数阻塞自己的线程以等待创建的线程,只是为了利用 try / catch,那么这基本上会使创建新线程变得无用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接