gpt4 book ai didi

c# - 在另一个线程上捕获异常?

转载 作者:行者123 更新时间:2023-11-30 14:30:50 26 4
gpt4 key购买 nike

在开发 winform 应用程序时,您通常需要调用主 GUI 线程来执行 GUI 工作。

Invoke 如今已过时(如果我没看错的话),您应该改为使用 SynchronizationContext

问题是:我如何处理异常?我注意到有时在“同步/调用”线程上抛出的异常会丢失?

我确实使用了 Application.ThreadExceptionAppDomain.CurrentDomain.UnhandledException 但这没有帮助吗?

最佳答案

首先,Synchronization Context 并不是什么新鲜事物,它自 .NET 2.0 以来就一直存在。它与异常处理特别是无关。它也不会使 Control.Invoke 过时。事实上,WinFormsSynchronizationContext 是 WinForms 的同步上下文实现,它使用 Control.BeginInvoke 进行 PostControl.Invoke 用于 Send 方法。

How do I handle exceptions? I have notice that sometimes the exception thrown on the "synced/invoked" thread is lost?

这里的“有时”背后有一个有据可查的行为。 Control.Invoke 是一个同步调用,它将异常从回调内部传播到调用线程:

int Test()
{
throw new InvalidOperationException("Surpise from the UI thread!");
}

void Form_Load(object sender, EventArgs e)
{
// UI thread
ThreadPool.QueueUserWorkItem(x =>
{
// pool thread
try
{
this.Invoke((MethodInvoker)Test);
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
});
}

使用 SynchronizationContext 的好处在于解耦 WinForms 细节。这对于可移植库来说很有意义,它可能被 WinForms、WPF、Windows Phone、Xamarin 或任何其他客户端使用:

// UI thread
var uiSynchronizationContext = System.Threading.SynchronizationContext.Current;
if (uiSynchronizationContext == null)
throw new NullReferenceException("SynchronizationContext.Current");

ThreadPool.QueueUserWorkItem(x =>
{
// pool thread
try
{
uiSynchronizationContext.Send(s => Test(), null);
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
});

因此,使用 Control.Invoke(或 SynchronizationContext.Send),您可以选择在调用线程上处理异常。根据设计和常识,Control.BeginInvoke(或 SynchronizationContext.Post)没有这样的选项。这是因为 Control.BeginInvoke 是异步的,它将回调排队,以便在 Application.Run 运行的消息循环的 future 迭代时执行。

要能够处理由异步回调 抛出的异常,您需要实际观察异步操作的完成。在 C# 5.0 之前,您可以为此使用事件或 Task.ContinueWith

使用事件:

class ErrorEventArgs : EventArgs
{
public Exception Exception { get; set; }
}

event EventHandler<ErrorEventArgs> Error = delegate { };

void Form_Load(object sender, EventArgs e)
{
this.Error += (sError, eError) =>
// handle the error on the UI thread
Debug.Print(eError.Exception.ToString());

ThreadPool.QueueUserWorkItem(x =>
{
this.BeginInvoke(new MethodInvoker(() =>
{
try
{
Test();
}
catch (Exception ex)
{
// fire the Error event
this.Error(this, new ErrorEventArgs { Exception = ex });
}
}));
});
}

使用 ContinueWith:

ThreadPool.QueueUserWorkItem(x =>
{
var tcs = new TaskCompletionSource<int>();

uiSynchronizationContext.Post(s =>
{
try
{
tcs.SetResult(Test());
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}, null);

// observe the completion,
// only if there's an error
tcs.Task.ContinueWith(task =>
{
// handle the error on a pool thread
Debug.Print(task.Exception.ToString());
}, TaskContinuationOptions.OnlyOnFaulted);

});

最后,在 C# 5.0 中,您可以使用 async/await 并使用 try/catch 一样方便地处理异步抛出的异常用于同步调用:

int Test()
{
throw new InvalidOperationException("Surpise from the UI thread!");
}

async void Form_Load(object sender, EventArgs e)
{
// UI thread
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Run(async () =>
{
// pool thread
try
{
await Task.Factory.StartNew(
() => Test(),
CancellationToken.None,
TaskCreationOptions.None,
uiTaskScheduler);
}
catch (Exception ex)
{
// handle the error on a pool thread
Debug.Print(ex.ToString());
}
});
}

关于c# - 在另一个线程上捕获异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21885216/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com