来自UI线程中的Async组件的Fire事件

我正在.Net 2.0中构建一个非可视组件。 该组件使用异步套接字(BeginReceive,EndReceive等)。 异步回调在运行时创建的工作线程的上下文中调用。 组件用户不必担心multithreading(这是主要目标,我想要的)

组件用户可以在任何线程中创建我的非可视组件(UI线程只是简单应用程序的通用线程。更严重的应用程序可以在任意工作线程中创建组件)。 组件触发事件,例如“SessionConnected”或“DataAvailable”。

问题:由于Async Callbacks和其中引发的事件,事件处理程序在工作线程上下文中执行。 我想使用一个中间层,它强制事件处理程序在首先创建组件的线程的上下文中执行。

示例代码(从exception处理等中删除…)

///  /// Occurs when the connection is ended ///  /// The IAsyncResult to read the information from private void EndConnect(IAsyncResult ar) { // pass connection status with event this.Socket.EndConnect(ar); this.Stream = new NetworkStream(this.Socket); // -- FIRE CONNECTED EVENT HERE -- // Setup Receive Callback this.Receive(); } ///  /// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect ///  /// The IAsyncResult that was used by BeginRead private void EndReceive(IAsyncResult ar) { int nBytes; nBytes = this.Stream.EndRead(ar); if (nBytes > 0) { // -- FIRE RECEIVED DATA EVENT HERE -- // Setup next Receive Callback if (this.Connected) this.Receive(); } else { this.Disconnect(); } } 

由于Async套接字的性质,使用我的组件的所有应用程序都散布着“If(this.InvokeRequired){…”,我想要的只是用户能够无忧无虑地使用我的组件作为一种丢弃-在。

那么我如何在不要求用户检查InvokeRequired的情况下提升事件(或者换句话说,如何强制在与首先发起事件的线程相同的线程中引发的事件)?

我已经阅读过有关AsyncOperation,BackgroundWorkers,SynchronizingObjects,AsyncCallbacks以及大量其他内容的内容,但这些都让我头晕目眩。

我确实提出了这个,肯定是笨拙的“解决方案”,但在某些情况下它似乎失败了(例如当我通过静态类从WinForms项目调用我的组件时)

  ///  /// Raises an event, ensuring BeginInvoke is called for controls that require invoke ///  ///  ///  /// http://www.eggheadcafe.com/articles/20060727.asp protected void RaiseEvent(Delegate eventDelegate, object[] args) { if (eventDelegate != null) { try { Control ed = eventDelegate.Target as Control; if ((ed != null) && (ed.InvokeRequired)) ed.Invoke(eventDelegate, args); else eventDelegate.DynamicInvoke(args); } catch (Exception ex) { Console.WriteLine(ex.GetType()); Console.WriteLine(ex.Message); //Swallow } } } 

任何帮助,将不胜感激。 提前致谢!

编辑:根据这个线程,我最好的选择是使用SyncrhonizationContext.Post,但我看不到如何将它应用于我的情况。

好; 所以这是我在阅读之后得到的结论:

 public class MyComponent { private AsyncOperation _asyncOperation; /// Constructor of my component: MyComponent() { _asyncOperation = AsyncOperationManager.CreateOperation(null); } ///  /// Raises an event, ensuring the correct context ///  ///  ///  protected void RaiseEvent(Delegate eventDelegate, object[] args) { if (eventDelegate != null) { _asyncOperation.Post(new System.Threading.SendOrPostCallback( delegate(object argobj) { eventDelegate.DynamicInvoke(argobj as object[]); }), args); } } } 

这里发布的另一个解决方案是正在进行的工作。 这里发布的解决方案似乎(根据MSDN)是迄今为止最好的解决方案。 建议非常非常欢迎。

我好像找到了我的解决方案:

  private SynchronizationContext _currentcontext /// Constructor of my component: MyComponent() { _currentcontext = WindowsFormsSynchronizationContext.Current; //...or...? _currentcontext = SynchronizationContext.Current; } ///  /// Raises an event, ensuring the correct context ///  ///  ///  protected void RaiseEvent(Delegate eventDelegate, object[] args) { if (eventDelegate != null) { if (_currentcontext != null) _currentcontext.Post(new System.Threading.SendOrPostCallback( delegate(object a) { eventDelegate.DynamicInvoke(a as object[]); }), args); else eventDelegate.DynamicInvoke(args); } } 

我还在测试这个,但似乎工作正常。

也许我不理解这个问题,但在我看来,你可以在Async状态下传递对自定义对象的引用。

我把以下例子放在一起来说明;

首先我们有一个Callback对象。 这有2个属性 – 一个用于调度动作的控件和一个要调用的动作;

 public class Callback { public Control Control { get; set; } public Action Method { get; set; } } 

然后我有一个WinForms项目,在另一个线程上调用一些随机代码(使用BeginInvoke),然后在代码完成执行时显示一个消息框。

  private void Form1_Load(object sender, EventArgs e) { Action act = (bool myBool) => { Thread.Sleep(5000); }; act.BeginInvoke(true, new AsyncCallback((IAsyncResult result) => { Callback c = result.AsyncState as Callback; c.Control.Invoke(c.Method); }), new Callback() { Control = this, Method = () => { ShowMessageBox(); } }); } 

ShowMessageBox方法必须在UI线程上运行,如下所示:

  private void ShowMessageBox() { MessageBox.Show("Testing"); } 

这是你在寻找什么?

如果您的组件必须始终由同一个线程使用,您可以执行以下操作:

 public delegate void CallbackInvoker(Delegate method, params object[] args); public YourComponent(CallbackInvoker invoker) { m_invoker = invoker; } protected void RaiseEvent(Delegate eventDelegate, object[] args) { if (eventDelegate != null) { try { if (m_invoker != null) m_invoker(eventDelegate, args); else eventDelegate.DynamicInvoke(args); } catch (Exception ex) { Console.WriteLine(ex.GetType()); Console.WriteLine(ex.Message); //Swallow } } } 

然后,当您从窗体或其他控件实例化组件时,您可以执行以下操作:

 YourComponent c = new YourComponent(this.Invoke); 

要在非UI工作线程上对事件进行排队,它必须具有某种工作排队机制,然后您可以为具有CallbackInvoker签名的方法提供在工作线程上对委托进行排队的方法。