C# 如何使事件回调到win窗体线程安全?
从表单中订阅对象上的事件时,实际上是将回调方法的控制权移交给事件源。您不知道该事件源是否会选择在其他线程上触发该事件C# 如何使事件回调到win窗体线程安全?,c#,.net,winforms,multithreading,events,C#,.net,Winforms,Multithreading,Events,从表单中订阅对象上的事件时,实际上是将回调方法的控制权移交给事件源。您不知道该事件源是否会选择在其他线程上触发该事件 问题是,当调用回调时,您不能假设您可以在窗体上创建更新控件,因为如果在与窗体运行线程不同的线程上调用事件回调,这些控件有时会引发异常。以下是要点: 不能从创建UI控件的线程(窗体的线程)以外的线程进行UI控件调用 委托调用(即事件挂钩)与触发事件的对象在同一线程上触发 因此,如果您有一个单独的“引擎”线程做一些工作,并且有一些UI监视可以反映在UI中的状态更改(例如进度条或其他什
问题是,当调用回调时,您不能假设您可以在窗体上创建更新控件,因为如果在与窗体运行线程不同的线程上调用事件回调,这些控件有时会引发异常。以下是要点:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
其实很简单
这是一种非常简洁的方法来解决此问题,并使表单免受多线程事件回调的影响。在许多简单的情况下,您可以使用MethodInvoker委托,而无需创建自己的委托类型。为了稍微简化Simon的代码,您可以使用内置的通用操作委托。这样可以避免在代码中添加一些并不需要的委托类型。此外,在.NET3.5中,他们向Invoke方法添加了一个params参数,因此您不必定义临时数组
void SomethingHappened(object sender, EventArgs ea)
{
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
return;
}
textBox1.Text = "Something happened";
}
void somethingOccessed(对象发送方,事件参数)
{
如果(需要调用)
{
调用(新操作(发生了某些事情)、发送方、ea);
返回;
}
textBox1.Text=“发生了什么事”;
}
在这种情况下,我经常使用匿名方法:
void SomethingHappened(object sender, EventArgs ea)
{
MethodInvoker del = delegate{ textBox1.Text = "Something happened"; };
InvokeRequired ? Invoke( del ) : del();
}
关于这个话题,我有点晚了,但是你可能想看一下。当正确实现时,它保证总是从UI线程引发事件 下面是一个只允许一次并发调用的简单示例;支持多个调用/事件需要更多的管道
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class MainForm : Form
{
private TypeWithAsync _type;
[STAThread()]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
public MainForm()
{
_type = new TypeWithAsync();
_type.DoSomethingCompleted += DoSomethingCompleted;
var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };
var btn = new Button() { Text = "Synchronous" };
btn.Click += SyncClick;
panel.Controls.Add(btn);
btn = new Button { Text = "Asynchronous" };
btn.Click += AsyncClick;
panel.Controls.Add(btn);
Controls.Add(panel);
}
private void SyncClick(object sender, EventArgs e)
{
int value = _type.DoSomething();
MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
}
private void AsyncClick(object sender, EventArgs e)
{
_type.DoSomethingAsync();
}
private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
{
MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
}
}
class TypeWithAsync
{
private AsyncOperation _operation;
// synchronous version of method
public int DoSomething()
{
Thread.Sleep(5000);
return 27;
}
// async version of method
public void DoSomethingAsync()
{
if (_operation != null)
{
throw new InvalidOperationException("An async operation is already running.");
}
_operation = AsyncOperationManager.CreateOperation(null);
ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
}
// wrapper used by async method to call sync version of method, matches WaitCallback so it
// can be queued by the thread pool
private void DoSomethingAsyncCore(object state)
{
int returnValue = DoSomething();
var e = new DoSomethingCompletedEventArgs(returnValue);
_operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
}
// wrapper used so async method can raise the event; matches SendOrPostCallback
private void RaiseDoSomethingCompleted(object args)
{
OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
}
private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
{
var handler = DoSomethingCompleted;
if (handler != null) { handler(this, e); }
}
public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
}
public class DoSomethingCompletedEventArgs : EventArgs
{
private int _value;
public DoSomethingCompletedEventArgs(int value)
: base()
{
_value = value;
}
public int Value
{
get { return _value; }
}
}
}
使用系统;
使用系统组件模型;
使用系统线程;
使用System.Windows.Forms;
命名空间Windows窗体应用程序1
{
公共类主窗体:窗体
{
私有类型与异步类型;
[STAThread()]
公共静态void Main()
{
Application.EnableVisualStyles();
运行(新的MainForm());
}
公共表格(
{
_type=新类型WithAsync();
_type.DoSomethingCompleted+=DoSomethingCompleted;
var panel=newflowlayoutpanel(){Dock=DockStyle.Fill};
var btn=new Button(){Text=“Synchronous”};
点击+=同步点击;
面板.控件.添加(btn);
btn=新建按钮{Text=“Asynchronous”};
点击+=异步点击;
面板.控件.添加(btn);
控件。添加(面板);
}
私有void SyncClick(对象发送方,事件参数e)
{
int value=_type.DoSomething();
Show(string.Format(“DoSomething()返回{0}.”,value));
}
私有void异步单击(对象发送方,事件参数e)
{
_DoSomethingAsync()类型;
}
私有void DoSomethingCompleted(对象发送方,DoSomethingCompletedEventArgs e)
{
Show(string.Format(“DoSomethingAsync()返回{0}.”,e.Value));
}
}
类TypeWithAsync
{
私有异步操作\u操作;
//方法的同步版本
公共int DoSomething()
{
睡眠(5000);
返回27;
}
//方法的异步版本
公共void DoSomethingAsync()
{
如果(_操作!=null)
{
抛出新的InvalidOperationException(“异步操作已在运行”);
}
_operation=AsyncOperationManager.CreateOperation(空);
ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
}
//异步方法用于调用方法的同步版本的包装器,与WaitCallback匹配,以便
//可以由线程池排队
私有void DoSomethingAsyncCore(对象状态)
{
int returnValue=DoSomething();
var e=新剂量计完成剂量(返回值);
_操作。操作后完成(升起一些东西完成,e);
}
//用于使异步方法能够引发事件的包装器;匹配SendOrPostCallback
私有void raisedomethingcompleted(对象参数)
{
OnDoSomething已完成((doSomething CompletedEventArgs)参数);
}
已完成的专用void ondosmething(dosomething completedeventargs e)
{
var handler=DoSomethingCompleted;
if(h)
private void DoInvoke(MethodInvoker del) {
if (InvokeRequired) {
Invoke(del);
} else {
del();
}
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
DoInvoke(delegate { lbl.Text = val; });
}
private void directPass() {
DoInvoke(this.directInvoke);
}
private void directInvoke() {
textLabel.Text = "Directly passed.";
}