C# 如何在目标线程上运行事件处理程序
给出C#代码示例:C# 如何在目标线程上运行事件处理程序,c#,multithreading,winforms,events,C#,Multithreading,Winforms,Events,给出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(Obje
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;
}
使用系统;
使用系统线程;
使用System.Windows.Forms;
公共类MnFrm:表格
{
私有void MnFrm_加载(对象发送方,事件参数e)
{
this.WorkCompleted+=MnFrm_WorkCompleted;
}
私有无效btn_单击(对象发送者,事件参数e)
{
QueueUserWorkItem(异步方法);
}
私有void MnFrm_工作已完成(对象发送方,布尔值e)
{
MessageBox.Show(“工作完成”);
}
私有void异步方法(对象状态)
{
//做事
Boolean result=true;//仅作为示例
工作完成?调用(此,结果);
}
私有事件EventHandler工作已完成;
}
当用户单击按钮btn
时,将在线程池管理的另一个线程上执行方法AsyncMethod
。一段时间后,工作完成,结果通过另一个事件发回。
此事件处理程序(WorkCompleted
)在用于运行asynchmethod
的线程上执行,因为执行应用程序时会出现“交叉线程”异常
因此,问题是如何在UI线程上运行事件处理程序
MnFrm\u WorkCompleted
,,如果将其更改为:
this.Invoke(new Action(() =>
{
WorkCompleted?.Invoke(this, result);
});
它会起作用的。这是因为表单包含一个方法Invoke()
,该方法将在创建它的线程上调用该方法。事件的调用只不过是调用委托
阅读表格moet信息
您可以使用invokererequired
来确定您的线程是否正确,但我认为这会增加开销,并降低可读性。始终使用此选项。调用
BeginInvoke
,这在将消息“发布”到UI线程时非常有用。唯一的问题是,当线程引发许多事件而UI线程没有足够的时间处理它们时,您无法看到有多少事件排队
第三种方法是使用队列。当事件发生时,将消息添加到队列(我将使用列表
),并使用表单计时器处理队列。其优点是,UI线程不会暂停线程,而是在将其添加到队列后直接继续。您的应用程序将被暂停。您可以使用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之间的差异:
谢谢大家
Jeroen van Langen的回答非常正确!成功了。事实上,我是这样做的,但为了防止偏见,我不想发布解决方案——我想看到其他解决方案
但是,我更喜欢Panagiotis Kanavos的答案,他建议使用var result=wait Task.Run(…)
。这完美地清除了代码!谢谢你卡纳沃斯松鸡 正确的解决方案将取决于AsyncMethod
实际执行的操作。如果方法访问某些外部资源,则可以将代码更改为在一个线程上有效运行,而不必担心线程/调用和其他多线程相关问题。为什么要使用QueueUserWorkItem
而不是var result=wait Task.run(…)
?它在所有受支持的.NET版本中都可用,并将所有这些代码转换为正确处理异步操作的单行代码。最早支持的.NET版本是4.5.2<代码>等待
是在4.5中添加的。4.0中的任务即使您需要从其他线程报告某些内容,也不要使用事件。这就是IProgress接口和progress
类的工作