鉴于 C#代码示例:
using System;
using System.Threading;
using System.Windows.Forms;
public class MnFrm : Form
{
private void MnFrm_Load(Object sender, EventArgs e)
{
this.WorkCompleted += MnFrm_WorkCompleted;
}
private void btn_Click(Object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(AsyncMethod);
}
private void MnFrm_WorkCompleted(Object sender, Boolean e)
{
MessageBox.Show("Work completed");
}
private void AsyncMethod(Object state)
{
// Do stuff
Boolean result = true; // just as an example
WorkCompleted?.Invoke(this, result);
}
private event EventHandler<Boolean> WorkCompleted;
}
当用户单击按钮btn
时,方法AsyncMethod
将在ThreadPool管理的另一个线程上执行。一段时间后,工作完成,结果通过另一个事件回发。
此事件处理程序(WorkCompleted
)在用于运行AsyncMethod
的线程上执行,因为当应用程序执行时,您将获得“交叉线程”&#39;异常。
所以问题是如何在UI线程上运行事件处理程序MnFrm_WorkCompleted
?
答案 0 :(得分:2)
您可以使用Control.Invoke或Control.BeginInvoke方法在UI线程上调用特定方法。 请尝试以下代码:
private void MnFrm_WorkCompleted(Object sender, Boolean e)
{
if (InvokeRequired)
{
Invoke((Action) (() => MnFrm_WorkCompleted(sender, e)));
return;
}
MessageBox.Show("Work completed");
}
对于Invoke和BeginInvoke之间的区别: What's the difference between Invoke() and BeginInvoke()
答案 1 :(得分:0)
如果您将其更改为:
this.Invoke(new Action(() =>
{
WorkCompleted?.Invoke(this, result);
});
它会起作用。这是因为Form包含一个方法Invoke()
,它将在创建它的线程上调用该方法。事件的调用只不过是调用代理。
阅读here表单moet info。
您可以使用InvokeRequired
来确定您是否使用了正确的主题,但我认为它的开销会降低其可读性。始终使用this.Invoke
。
BeginInvoke
,这在发布&#39;时非常有用。给UI线程的消息。唯一的问题是,当线程引发许多事件并且UI线程没有足够的时间来处理它们时,您无法查看有多少事件排队。
第三种方法是使用队列。当事件发生时,将消息添加到队列(我将使用List<>
)并使用表单计时器来处理队列。优点是线程不会被UI线程停止,并在将其添加到队列后直接继续。而你的申请将被停滞。
答案 2 :(得分:0)
全部谢谢!
Jeroen van Langen 的答案非常正确!有效。实际上我是这样做的,但不想发布解决方案以防止偏见 - 我希望看到替代解决方案。
但是,我更喜欢 Panagiotis Kanavos 的答案,他建议使用var result=await Task.Run(...)
。这美妙地清理了代码!谢谢 Panagiotis Kanavos !!!