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

2023-09-03 22:10:19 作者:执笔ㄣ续写承诺

的主要问题是,从一个线程引发事件可以调用代表只应在一定的线程上下文调用。做一些研究this问题我想,也许它可能通过某种同步上下文以及每个订阅的事件:

The main problem is, that raising an event from one thread can invoke delegates which should only invoked in a certain thread context. After doing some research on this problem i thought, maybe its possible to pass some kind of synchronization context along with each subscription to an event:

SomeClass.SmartSyncEvent += (myDelegate, someReferenceToAThread);

,然后引发该事件,并以某种方式做:

and then raise the event and it somehow does:

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

这是超级伪code,但也许有人已经做了这样的事情,或者知道任何.NET类可以建立这样一个格局。 谢谢!

This is super pseudo code, but maybe someone has already done things like this, or knows of any .NET classes which can set up such a pattern. thanks!

推荐答案

要做到这一点,最好的方法是通过 SomeClass的通过同步上下文 ISynchronizeInvoke

The best way to do this is pass SomeClass a synchronization context via ISynchronizeInvoke.

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的的实现方式。它存在的问题是,每一个订户将被编组到同一同步对象。它不会出现,这是你想要的,虽然事情。

This pattern is similar to the way System.Timers.Timer is implemented. The problem with it is that every subscriber will be marshaled onto the same synchronizing object. It does not appear that this is what you want though.

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

Fortunately delegates store the class instance upon which the method should be invoke via the Target property. We can exploit this by extracting it before we invoke the delegate and using it as the synchronizing object assuming of course that it is a ISynchronizeInvoke itself. We could actually enforce that by using custom add and remove event accessors.

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 是静态方法无效。这就是为什么我执行与自定义添加约束访问。

This is a lot better in that each subscriber can be marshaled independently of the others. The problem with it is that the handlers must be instance methods that reside in an ISynchronizeInvoke class. Also, Delegate.Target is null for static methods. This is why I enforce that constraint with the custom add accessor.

你可以把它执行的同步处理,如果 Delegate.Target 为空或以其他方式不能被铸造成有用的同步对象。有对这个主题有很多变化。的

You could make it execute the handlers synchronously if Delegate.Target were null or otherwise could not be casted into a useful synchronizing object. There are a lot of variations on this theme.

而不是 ISynchronizeInvoke 的在WPF中,你可以$ C $下 DispatcherObject的。的

In WPF you could code for DispatcherObject instead of ISynchronizeInvoke.