Многократная подписка на событие с помощью EventHandlerList

#c# #events #sync

#c# #Мероприятия #синхронизация

Вопрос:

Цель: я хочу подписаться много раз на одно и то же событие в разных классах. У меня есть следующий класс:

 public class MyEngine : IEngine
{
    private EventHandlerList _events = new EventHandlerList();

    protected EventHandlerList Events
    {
        get
        {
            return _events;
        }
    }

    private readonly string OnReceivedEventName = "OnReceived";

    public event MessageEventHandler OnReceived
    {
        add
        {
            lock (this)
            {
                Events.AddHandler(OnReceivedEventName, value);
            }
        }
        remove
        {
            lock (this)
            {
                Events.RemoveHandler(OnReceivedEventName, value);
            }
        }
    }

    protected internal virtual void MessageReceived(MessageEventArgs e)
    {

        var handler = Events[OnReceivedEventName] as MessageEventHandler;
        TryFireMessageEvent(handler, e);
    }

    private void TryFireMessageEvent(MessageEventHandler handler, MessageEventArgs e)
    {
        try
        {
            if (handler != null)
                handler.Invoke(this, e);
        }
        catch (Exception ex)
        {
            log.Error("Message Received - Exception caught", ex);
            ErrorOccurred(ex);
        }
    }
}
  

Основной модуль имеет экземпляр IEngine:

 public class MyProvider
{
    protected internal IEngine Engine { get; set; }
    public MyProvider(IEngine engine)
    {
        Engine = engine;
        engine.OnReceived  = Engine_Received;
    }
    protected internal void Engine_Received(IEngine engine, MessageEventArgs args)
    {...}
}
  

У меня также есть функция ‘doAction’, которая создает операцию и внедряет движок:

 public class MyProvider
{
    ....
    public Result DoAction()
    {
        using (var operation = new SyncOperation(Engine))
        {
            operationResult = operation.Execute();
        }
    }
}
  

Синхронизация:

 public class SyncOperation : IDisposable
{
    private IEngine _engine;

    public SyncOperation (IEngine engine)
    {
        Ensure.NotNull(engine, "engine");
        _engine = engine;

        _engine.OnReceived  = Engine_OnReceived;
    }

    internal void Engine_OnReceived(IEngine engine, MessageEventArgs args)
    {...}
}
  

При вызове операции.Выполнить(); не все полученные события создаются в классе синхронизации.

Я что-то упускаю?

Комментарии:

1. Совсем немного coce, и я все еще не уверен, что у нас есть обзор… Может быть, опубликовать TryFireMessageEvent тоже?

Ответ №1:

Я сравнил ваш код с известным примером, и у меня есть сомнения по поводу private readonly string OnReceivedEventName как объектного ключа. Здесь вы полагаетесь на интернирование строк, «обычный» — это статический объект.

И это заставляет меня задуматься, являются ли обычные сгенерированные события потокобезопасными. Не удалось так быстро найти ссылку, но в качестве диагностики:

 public event MessageEventHandler OnReceived;
  

Тогда у вас может возникнуть состояние гонки, но это будет очень редко (и его легко минимизировать / устранить). Вызываются ли все обработчики?

Комментарии:

1. Я не думаю, что вызываются все обработчики. иногда вызывается обработчик синхронизации, а иногда вызывается обработчик MyProvider

2. @Anibas: Я имел в виду с ; на месте.

3. Спасибо! Не могли бы вы более подробно объяснить, в чем была проблема?

4. Тогда это было где-то в add / remove и связанном с ним коде. Я предполагаю использование string в качестве ключа объекта, но это было бы сомнительно только с несколькими сборками.