简体   繁体   English

在哪里处理Task抛出的异常

[英]Where to handle exceptions thrown by Task

I am executing some polling IO loops in separate Tasks. 我在单独的任务中执行一些轮询IO循环。 Those loops may encounter exceptions. 这些循环可能会遇到异常。 If one encounters an exception, I want to alert the caller so that it can: 如果遇到异常,我想提醒调用者,以便它可以:

  1. log it 记录下来
  2. kill all IO threads 杀死所有IO线程
  3. reset the connection 重置连接
  4. restart IO threads 重启IO线程

The UI must remain responsive. 用户界面必须保持响应。 What is the preferred method of handling this scenario? 处理此方案的首选方法是什么? I've include an illustrative program below. 我在下面列出了一个说明性的程序。

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

namespace TaskExceptionCatching
{
    class Program
    {
        static void Main(string[] args)
        {
            startLoops();

            System.Console.WriteLine("Type 'Exit' when you're ready to stop.");
            while (System.Console.ReadLine() != "Exit")
            {
                System.Console.WriteLine("Seriously, just type 'Exit' when you're ready to stop.");
            }
        }

        static private void startLoops()
        {
            System.Console.WriteLine("Starting fizzLoop.");
            var fizzTask = Task.Factory.StartNew(new Action(fizzLoop));

            System.Console.WriteLine("Starting buzzLoop.");
            var buzzTask = Task.Factory.StartNew(new Action(buzzLoop));
        }

        static private void fizzLoop()
        {
            while (true)
            {
                //simulate something error prone, like some risky IO
                System.Threading.Thread.Sleep(200);
                bool isErr = (new Random().Next(1, 100) == 10);
                if (isErr)
                    throw new Exception("Fizz got an exception.");
            }
        }

        static private void buzzLoop()
        {
            while (true)
            {
                //simulate something error prone, like some risky IO
                System.Threading.Thread.Sleep(200);
                bool isErr = (new Random().Next(1, 100) == 10);
                if (isErr)
                    throw new Exception("Buzz got an exception.");
            }
        }
    }
}

This might be one of the rare cases when an async void method could be convenient: 这可能是async void方法可能很方便的极少数情况之一:

static async void StartAndMonitorAsync(Func<Task> taskFunc)
{
    while (true)
    {
        var task = taskFunc();
        try
        {
            await task;
            // process the result if needed
            return;
        }
        catch (Exception ex)
        {
            // log the error
            System.Console.WriteLine("Error: {0}, restarting...", ex.Message);
        }
        // do other stuff before restarting (if any)
    }
}

static private void startLoops()
{
    System.Console.WriteLine("Starting fizzLoop.");
    StartAndMonitorAsync(() => Task.Factory.StartNew(new Action(fizzLoop)));

    System.Console.WriteLine("Starting buzzLoop.");
    StartAndMonitorAsync(() => Task.Factory.StartNew(new Action(buzzLoop)));
}

If you can't use async/await , a similar logic can be implemented using Task.ContinueWith . 如果你不能使用async/await ,可以使用Task.ContinueWith实现类似的逻辑。

If startLoops can be called multiple times (while tasks are already "in-flight"), you'd need to add cancellation logic to StartAndMonitorAsync and the tasks it sarts, using CancelltionToken (more details in "A pattern for self-cancelling and restarting task" ). 如果可以多次调用startLoops (当任务已经“在飞行中”时),则需要使用CancelltionTokenStartAndMonitorAsync及其启动的任务添加取消逻辑(更多细节见“自我取消和重启的模式”)任务“ )。

From MSDN "The Task infrastructure wraps them in an AggregateException instance. The AggregateException has an InnerExceptions property that can be enumerated to examine all the original exceptions that were thrown, and handle (or not handle) each one individually. Even if only one exception is thrown, it is still wrapped in an AggregateException." 从MSDN“任务基础结构将它们包装在AggregateException实例中.AggregateException有一个InnerExceptions属性,可以枚举该属性以检查所有抛出的原始异常,并单独处理(或不处理)每个异常。即使只有一个例外是抛出,它仍然包含在AggregateException中。“ For more information you can check this 有关详细信息,请查看此信息

try
{
System.Console.WriteLine("Starting fizzLoop.");
            var fizzTask = Task.Factory.StartNew(new Action(fizzLoop));

            System.Console.WriteLine("Starting buzzLoop.");
            var buzzTask = Task.Factory.StartNew(new Action(buzzLoop));

}
catch (AggregateException ae)
{
    // Assume we know what's going on with this particular exception. 
    // Rethrow anything else. AggregateException.Handle provides 
    // another way to express this. See later example. 
    foreach (var e in ae.InnerExceptions)
    {
        if (e is MyCustomException)
        {
            Console.WriteLine(e.Message);
        }
        else
        {
            throw;
        }
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM