线程完成后捕获异常 [英] Catch exception after thread is done

查看:87
本文介绍了线程完成后捕获异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在单独的线程中运行一些代码,这可能会引发异常(毕竟,代码倾向于这样做).该线程将从主线程(GUI)派生,因此最终必须在此处理异常(例如,设置错误消息文本块).我有两种解决方案,但都不允许在GUI线程中直接捕获异常.

I am running some code in a separate thread, which might throw an exception (after all, code tends to do this). The thread will be spawned from the main thread (GUI) so this is where the exceptions will eventually have to be handled (like setting an error message text block). I have two solutions, but neither allows direct catching of exceptions in the GUI thread.

注意:我不能使用 Task BackgroundWorker 之类的东西(至少不要开箱即用),因为我需要能够更改线程的 ApartmentState .

这就是我想要的:

var thread = new Thread(() =>
{
    // Code which can throw exceptions
});

try 
{
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    MethodThatAwaitsThread(thread);
}
catch
{
    // Exception handling in the GUI thread
}

这是行不通的,因为异常永远不会离开线程.我知道它不能随时离开线程,但是我可以等待线程结束,然后再捕获它.

This does not work, as the exception never leaves the thread. I am aware that it cannot leave the thread at any time, but I am fine with waiting for the thread to end, and then catch it.

这是我当前的解决方案,它利用Dispatcher与GUI线程通信:

Here is my current solution, which utilizes the Dispatcher to talk to the GUI thread:

var thread = new Thread(() => 
{
    try 
    {
        // Code which can throw exceptions
        Dispatcher.Invoke(UpdateGuiAsSuccess);
    }
    catch (Exception ex)
    {
        Dispatcher.Invoke(UpdateGuiAsError);
    }
}

另一种解决方案是将Exception存储在对象中,然后在以后显式检查它.但这冒着人们忘记检查异常的风险:

An alternative solution is to store the Exception in an object and then explicitely check for it afterwards. But this comes at a risk of people forgetting to check the exception:

Exception ex = null;    

var thread = new Thread(() => 
{
    try 
    {
        // Code which can throw exceptions            
    }
    catch (Exception threadEx)
    {
        ex = threadEx;
    }
}

if (ex != null) 
{
    UpdateGuiAsError();
}
else 
{
    UpdateGuiAsSuccess();
}

工作线程死后,是否仍然可以在GUI线程中重新抛出该错误?

Is there anyway I can get the error to be re-thrown in the GUI thread, once the worker thread dies?

推荐答案

可以Task与STA线程一起使用(我认为这是您想要的).

You can use Task with an STA thread (which I assume is what you want).

为此,您可以编写一些辅助方法来在已设置为STA的线程上启动任务:

To do so, you can write some helper methods to start a task on a thread that has been set to STA:

public static class STATask
{
    /// <summary>
    /// Similar to Task.Run(), except this creates a task that runs on a thread
    /// in an STA apartment rather than Task's MTA apartment.
    /// </summary>
    /// <typeparam name="TResult">The return type of the task.</typeparam>
    /// <param name="function">The work to execute asynchronously.</param>
    /// <returns>A task object that represents the work queued to execute on an STA thread.</returns>

    [NotNull] public static Task<TResult> Run<TResult>([NotNull] Func<TResult> function)
    {
        var tcs = new TaskCompletionSource<TResult>();

        var thread = new Thread(() =>
        {
            try
            {
                tcs.SetResult(function());
            }

            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

        return tcs.Task;
    }

    /// <summary>
    /// Similar to Task.Run(), except this creates a task that runs on a thread
    /// in an STA apartment rather than Task's MTA apartment.
    /// </summary>
    /// <param name="action">The work to execute asynchronously.</param>
    /// <returns>A task object that represents the work queued to execute on an STA thread.</returns>

    [NotNull] public static Task Run([NotNull] Action action)
    {
        var tcs = new TaskCompletionSource<object>(); // Return type is irrelevant for an Action.

        var thread = new Thread(() =>
        {
            try
            {
                action();
                tcs.SetResult(null); // Irrelevant.
            }

            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

        return tcs.Task;
    }
}

一旦有了,就可以轻松创建STA任务,然后使用.ContinueWith()处理任务中引发的异常,或者使用await捕获异常.

Once you have that, you can easily create an STA task and then use .ContinueWith() to handle exceptions thrown in the task, or use await to catch the exceptions.

(注意:[NotNull]来自Resharper注释-如果您不使用Resharper,请将其删除.)

(Note: [NotNull] is from Resharper annotations - remove them if you're not using Resharper.)

这篇关于线程完成后捕获异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆