您能想到.NET中具有同步上下文的事件模式吗?

主要问题是,从一个线程引发事件可以调用应该只在某个线程上下文中调用的委托。 在对这个问题做了一些研究后,我想,也许它可以传递某种同步上下文以及每个订阅事件:

SomeClass.SmartSyncEvent += (myDelegate, someReferenceToAThread); 

然后提出事件,它以某种方式:

 foreach(subscriber) { someReferenceToAThread.Invoke(myDelegate); } 

这是超伪代码,但也许有人已经做过这样的事情,或者知道任何可以设置这种模式的.NET类。 谢谢!

执行此操作的最佳方法是通过ISynchronizeInvoke传递SomeClass同步上下文。

 public class SomeClass { public event EventHandler SmartSyncEvent; public ISynchronizeInvoke SynchronizingObject { get; set; } public void RaiseSmartSyncEvent() { if (SynchronizingObject != null) { SynchronizingObject.Invoke( (Action)(()=> { SmartSyncEvent(); }), null); } else { SmartSyncEvent(); } } } 

此模式类似于System.Timers.Timer的实现方式。 它的问题是每个用户将被编组到同一个同步对象上。 看起来这不是你想要的。

幸运的是,委托存储了应该通过Target属性调用该方法的类实例。 我们可以通过在调用委托之前提取它并将其用作同步对象来利用它,当然假设它是一个ISynchronizeInvoke本身。 我们实际上可以通过使用自定义添加和删除事件访问器来强制执行。

 public class SomeClass { private EventHandler _SmartSyncEvent; public event EventHandler SmartSyncEvent { add { if (!(value.Target is ISynchronizeInvoke)) { throw new ArgumentException(); } _SmartSyncEvent = (EventHandler)Delegate.Combine(_SmartSyncEvent, value); } remove { _SmartSyncEvent = (EventHandler)Delegate.Remove(_SmartSyncEvent, value); } } public void RaiseMyEvent() { foreach (EventHandler handler in _SmartSyncEvent.GetInvocationList()) { var capture = handler; var synchronizingObject = (ISynchronizeInvoke)handler.Target; synchronizingObject.Invoke( (Action)(() => { capture(this, new EventArgs()); }), null); } } } 

这样做要好得多,因为每个用户可以独立于其他用户进行编组。 它的问题是处理程序必须是驻留在ISynchronizeInvoke类中的实例方法。 此外, Delegate.Target对于静态方法为null。 这就是我使用自定义add访问器强制执行该约束的原因。


如果Delegate.Target为null或者无法将其转换为有用的同步对象,则可以使其同步执行处理程序。 这个主题有很多变化。

在WPF中,您可以为DispatcherObject而不是ISynchronizeInvoke编写代码。

请参阅本主题中的答案 。 我提供了3个博客文章和我编写的YouTube教学video的链接,所有这些都是关于与UI线程安全同步的。 每篇博文都涵盖了一种完全不同的方法,可以满足您的要求,因此您可以使用哪种方法。 有些比其他更容易,但通常都是中间的。

编辑引自链接的问题。

你实际上有很多选择。

(1) BackgroundWorker。 如果你真的想要在WinForms中进行异步工作的最简单的编程模型,那就是这个。 它通常用于执行某些异步任务并报告进度,但如果您不需要,则不必报告进度。

(2) 基于事件的异步模式。 如果您想制作一个完整的组件来执行某些异步任务,完全控制报告进度和您自己的自定义事件,那么这是一种方法。 这也有助于您理解线程,而不是BackgroundWorker。 因为我是一个视觉人, 所以我创建了一个完整的video演示文稿,介绍如何使用WinForms进行此操作 。

(3) 任务并行库。 您可以将TPL与WinForms一起使用,我在这里写了一篇非常详细的博客文章,介绍如何做到这一点 。

(4) 异步和等待 。 请注意,这需要.NET 4.5,C#5.0和C#5.0编译器仅包含在Visual Studio 11中,Visual Studio 11目前仅在BETA中。 但是,我还有一篇关于如何做到这一点的完整博客文章 。

(5) 带线程的ISynchronizedInvoke。 这是另一种选择, 我也有一个完整的博客 。

这取决于您选择哪种方法。 我的建议是简要介绍每一个,并根据主题对您的感受,或者哪种方法最符合您的要求,选择一种方法。