Как проверить, был ли уничтожен ManualResetEvent, при попытке установить () его внутри EventHandler? - PullRequest
11 голосов
/ 25 января 2012

У меня есть следующий шаблон дизайна:

    var myObjectWithEvents = new ObjectWithEvents();
    using (var mre = new ManualResetEvent(false)) {
        var onEvent = new EventHandler<EventArgs>((sender, e) => { mre.Set(); });   
        try {
            myObjectWithEvents.OnEvent += onEvent;
            var task = Task.Factory.StartNew(() => {
                myObjectWithEvents.DoSomethingThatShouldRaiseAnEvent();
            });
            var timedOut = !mre.WaitOne(10000);
        }
        finally {
            myObjectWithEvents.OnEvent -= onEvent;
        }
    }

Моя проблема заключается в том, что если OnEvent возникает после истечения времени ожидания WaitOne и выхода из блока использования, локальный обработчик события onEvent будет по-прежнему вызываться и попытаться установить ManualResetEvent mre, который будет уже утилизирован, хотя onEvent должен был быть незарегистрирован с OnEvent.

Простой обходной путь - проверить, был ли mre уже удален, но, к сожалению, такого поля нет, и я считаю, что перенос mre.Set() внутри блока try catch для игнорирования исключения не является чистым, учитывая, что это исключение может происходить довольно часто.

Что бы вы предложили в качестве наилучшего и самого простого способа достижения цели вышеуказанного шаблона кода (т. Е. Ожидания события, которое будет инициировано), не сталкиваясь с проблемой такого рода?

Редактировать: Благодаря вашим ответам я создал следующее расширение и заменил mre.Set() на mre.TrySet():

    public static void TrySet(this ManualResetEvent mre) {
        if (!mre.SafeWaitHandle.IsClosed) mre.Set();
    }

Ответы [ 3 ]

9 голосов
/ 25 января 2012
ManualResetEvent.SafeWaitHandle.IsClosed

Кажется странным, но единственное, что делает утилизация, это закрывает safeHandler, который является единственным объектом, для которого предназначена утилизация ...

Утилизация SafeWaitHandle изменяет это свойство с False на True.

7 голосов
/ 25 января 2012

Вы можете попробовать проверить это с помощью свойства mre.SafeWaitHandle.IsClosed

1 голос
/ 25 января 2012

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

bool isMreSync = true;
var myObjectWithEvents = new ObjectWithEvents();
using (var mre = new ManualResetEvent(false)) 
{
    var onEvent = new EventHandler<EventArgs>((sender, e) => 
                 { 
                     if (isMreSync)
                     {
                         mre.Set(); 
                     }
                 });

  // try ... finally block  
 }

isMreSync = false;

Если событие может быть выполнено асинхронно - синхронизируйте доступ к логическому коммутатору.

...