Утилизируйте WaitHandle для синхронизации основных потоков

Согласно документации, WaitHandle в.NET должен быть явно / неявно расположен. Однако у меня возникают проблемы с достижением этого для следующей основной задачи синхронизации:

  • трудоемкая задача выполняется в потоке.
  • основной поток ожидает завершения задачи в течение заданного периода времени. Основной поток должен продолжаться, если a. задание выполнено или б. истекло время ожидания.

Вот моя попытка использования объекта AutoResetEvent:

using(var waitHandle = new AutoResetEvent(false)){
    var worker = new Thread(() =>
    {
        try
        {
            TimeConsumingTask();
            waitHandle.Set(); //throws System.ObjectDisposedException: Safe handle has been closed
        }
        catch (Exception e)
        {...}
    }) {IsBackground = true};
    worker.Start(); //start worker

    const int waitTimeInMs = 5000; 
    var signaled = waitHandle.WaitOne(waitTimeInMs);//block main thread here. 
    if (!signaled)
    { //if timed out
       worker.Interrupt();
    }
}

Существует явное состояние состязания, когда время ожидания основного потока истекает, и объект дескриптора ожидания удаляется, что вызывает исключение ObjectDisposedException. Есть ли другой способ настроить это так, чтобы ручка была правильно расположена и не вызывала исключения?

2 ответа

Решение

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

Но вы сосредоточены на гораздо меньшей проблеме. Сам класс Thread уже является ресурсом, потребляющим мегабайт виртуальной машины и пять объектов синхронизации. Но у него нет метода Dispose(). Это был смелый дизайн, просто нет достойного способа вызвать метод.

Утилизация не обязательна, ничего страшного не происходит, когда вы ее не называете. Класс получил вашу поддержку, у него есть финализатор, который обеспечивает освобождение ресурса операционной системы. Который будет работать, в конце концов, просто не так быстро, как хотелось бы.

Сравните это с классом с менее смелым дизайном, у класса Task есть метод Dispose(). Который, как нить, почти так же сложно назвать. Руководство от.NET-гуру - просто не беспокоиться.

Тоже самое.

Дескриптор ожидания утилизируется, потому что используемая вами область вызывает новый поток и немедленно возвращает его, в результате чего дескриптор ожидания удаляется.

Что вы должны сделать, так это явно вызвать dispose после завершения работы вместо выражения using:

waitHandle.WaitOne(waitTimeInMs);
if (!signaled)
{ //if timed out
   worker.Interrupt();
}
waitHandle.Dispose();
Другие вопросы по тегам