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

本文关键字:上下文 事件 模式 同步 能想到 NET | 更新日期: 2023-09-27 18:33:15

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

SomeClass.SmartSyncEvent += (myDelegate, someReferenceToAThread);

然后引发事件,它以某种方式做到了:

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

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

你能想到在 .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访问器强制实施该约束的原因。


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

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

请看我在本主题中的回答。我提供了 3 篇博客文章和我编写的 YouTube 教学视频的链接,这些文章都讨论了与 UI 线程安全的同步。每篇博文都涵盖了一种完全不同的方法来执行您所要求的操作,因此由您使用哪种方法取决于您。有些比其他的容易,但通常都是中级的。

编辑引用自链接的问题。

你实际上有很多选择。

(1(背景工作者。如果你真的想要最简单的编程 WinForms 中异步工作的模型,就是这个。它 一般用于做一些异步任务和报告进度 不过,但如果不需要,则不必报告进度。

(2( 基于事件的异步模式。如果你想做一个完整的 执行某些异步任务的吹制组件,可以完全控制 报告进度和您自己的自定义事件,那么这是一种方法 做吧。这也可以帮助您了解线程而不是 后台工作者。因为我是一个视觉人,所以我创造了一个完整的 有关如何使用 WinForms 执行此操作的视频演示。

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

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

(5( 使用线程调用。这是另一种选择, 我也有一篇完整的博客。

选择

哪种方法实际上取决于您。我的建议是采取一个 简要介绍每种方法,并根据其先进程度选择方法 主题对您来说,或者哪种方法可能满足您的要求 最好的。