#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 в качестве ключа объекта, но это было бы сомнительно только с несколькими сборками.