Утилизируйте 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();