线程完成后捕获异常

Noc*_*ceo 1 c# wpf multithreading

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

注:我不能用这样的东西 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
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Exception ex = null;    

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

if (ex != null) 
{
    UpdateGuiAsError();
}
else 
{
    UpdateGuiAsSuccess();
}
Run Code Online (Sandbox Code Playgroud)

无论如何,一旦工作线程死亡,我是否可以在GUI线程中重新抛出该错误?

Mat*_*son 5

可以使用TaskSTA线程(我认为这是您想要的)。

为此,您可以编写一些帮助程序方法以在已设置为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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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