内线程异常处理

时间:2013-09-30 10:30:07

标签: c# .net multithreading

我有一个对象A,我想用对象的B方法执行新的线程。我可以使用Task.CreateNew等。问题是我不知道如何处理新线程中的异常。

一般来说,我想要的是内部线程与对象的B方法抛出异常,父对象A将捕获并关闭它的执行,以及对象B.

  • 我无法将代码添加到主循环中

  • 主循环完成后捕获异常是不可接受的,我想要 按时发现内部线程异常

有没有办法实现它?

在下面的代码中,我没有异常处理和主线程继续:

static void Main()
{
    Console.WriteLine("start");
    Task.Factory.StartNew(PrintTime, CancellationToken.None);

    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(1000);
    }
}

private static void PrintTime()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("inner thread i={0}",i+1);
        Thread.Sleep(1000);
    }

    throw new Exception("exception");
}

5 个答案:

答案 0 :(得分:2)

保留对任务实例的引用,并在准备好处理其结果时在其上调用Wait。在任务执行期间抛出的任何未处理的内部异常都将包含在AggregateException中,而{{3}}将从Wait方法抛出。

static void Main()
{
    Console.WriteLine("start");
    Task task = Task.Factory.StartNew(PrintTime, CancellationToken.None);

    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(1000);

        // Stop iterating in case of unhandled exception in inner task.
        if (task.Status == TaskStatus.Faulted)
            break;
    }

    try
    {
        task.Wait();
    }
    catch (AggregateException ae) 
    {
        ae.Handle((x) =>
        {
             Console.WriteLine("Exception: " + x.ToString());
        });
    }
}

答案 1 :(得分:1)

无论哪个线程最初抛出异常,以下解决方案都有效:

static void Main()
{
    Console.WriteLine("start");

    var innerCts = new CancellationTokenSource();
    Exception mainException = null;
    var mainThread = new Thread(() => SafeMainThread(innerCts, ref mainException));
    mainThread.Start();

    var innerTask = Task.Factory.StartNew(state => PrintTime(state),
                                          innerCts,
                                          innerCts.Token,
                                          TaskCreationOptions.LongRunning,
                                          TaskScheduler.Default);

    var innerFault = innerTask.ContinueWith(t => { Console.WriteLine("Inner thread caused " + t.Exception.InnerException.GetType().Name + ". Main thread is being aborted..."); mainThread.Abort(); },
                                            TaskContinuationOptions.OnlyOnFaulted);

    var innerCancelled = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread cancelled."),
                                                TaskContinuationOptions.OnlyOnCanceled);

    var innerSucceed = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread completed."),
                                              TaskContinuationOptions.OnlyOnRanToCompletion);

    try
    {
        innerTask.Wait();
    }
    catch (AggregateException)
    {
        // Ignore.
    }

    mainThread.Join();

    Console.ReadLine();
}

private static void SafeMainThread(CancellationTokenSource innerCts, ref Exception mainException)
{
    try
    {
        MainThread();
        Console.WriteLine("Main thread completed.");
    }
    catch (ThreadAbortException)
    {
        Console.WriteLine("Main thread aborted.");
    }
    catch (Exception exception)
    {
        Console.WriteLine("Main thread caused " + exception.GetType().Name + ". Inner task is being canceled...");

        innerCts.Cancel();
        mainException = exception;
    }
}

private static void MainThread()
{
    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(500);
    }
    throw new Exception("exception");
}

private static void PrintTime(object state)
{
    var cts = (CancellationTokenSource)state;

    for (int i = 0; i < 10; i++)
    {
        cts.Token.ThrowIfCancellationRequested();

        Console.WriteLine("inner thread i={0}", i + 1);
        Thread.Sleep(500);
    }

    throw new Exception("exception");
}

答案 2 :(得分:0)

尝试拦截内部异常并在主循环中检查其值,就好像是取消请求一样:

static void Main()
{
    Console.WriteLine("start");

    try
    {
        AggregateException innerException = null;

        Task.Factory.StartNew(PrintTime, CancellationToken.None)
                    .ContinueWith(t => innerException = t.Exception, TaskContinuationOptions.OnlyOnFaulted);

        for (int i = 0; i < 20; i++)
        {
            if (innerException != null)
                throw innerException;

            Console.WriteLine("master thread i={0}", i + 1);
            Thread.Sleep(1000);
        }
    }
    catch (AggregateException)
    {
        Console.WriteLine("Inner thread caused exception. Main thread handles that exception.");
    }
}

答案 3 :(得分:0)

您可以在发生异常时尝试运行异常处理程序。你可以使用一个标志,我在例子中使用异常来检查异常发生本身:

    private static AggregateException exception = null;

    static void Main(string[] args)
    {
        Console.WriteLine("Start");

        Task.Factory.StartNew(PrintTime, CancellationToken.None).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted);

        for (int i = 0; i < 20; i++)
        {
            Console.WriteLine("Master Thread i={0}", i + 1);
            Thread.Sleep(1000);
            if (exception != null)
            {
                break;
            }
        }

        Console.WriteLine("Finish");
        Console.ReadLine();
    }

    private static void HandleException(Task task)
    {
        exception = task.Exception;
        Console.WriteLine(exception);
    }

    private static void PrintTime()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Inner Thread i={0}", i + 1);
            Thread.Sleep(1000);
        }

        throw new Exception("exception");
    }

答案 4 :(得分:0)

如果你不能修改主体的内部,我不知道如何对它进行细粒度的控制。我现在看到的解决方案是将主体包装到托管线程中,如果内部线程抛出异常,则允许中止它:

static void Main()
{
    Console.WriteLine("start");

    var mainThread = new Thread(MainThread);
    mainThread.Start();

    var task = Task.Factory
                   .StartNew(PrintTime)
                   .ContinueWith(t => { Console.WriteLine("Inner thread caused exception. Main thread is being aborted."); mainThread.Abort(); },
                                 TaskContinuationOptions.OnlyOnFaulted);
    task.Wait();

    Console.WriteLine("Waiting for main thread to abort...");
    mainThread.Join();
    Console.WriteLine("Main thread aborted.");

    Console.ReadLine();
}

private static void MainThread()
{
    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(1000);
    }
}

private static void PrintTime()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("inner thread i={0}", i + 1);
        Thread.Sleep(1000);
    }

    throw new Exception("exception");
}