Мы были удивлены узнать сегодня что потоки, ожидающие на a ManualResetEvent
продолжите ожидать на событии, даже когда оно закрывается. Мы ожидали бы тот вызов Close()
неявно сигнализировал бы о потоках ожидания.
Мы разыскали это как причину, которую не закрывали некоторые наши сервисы окон с такой скоростью, как мы хотели бы. Мы изменяем весь наш Dispose
реализации то завершение ManualResetEvent
ссылки на вызов Set
сначала.
Может любой объяснять почему Close
неявно не звонит Set
? Когда Вы хотели бы, чтобы поток ожидания продолжил ожидать?
Вот наш тестовый код для демонстрации наших результатов:
private static readonly Stopwatch _timer = Stopwatch.StartNew();
public static void Test()
{
var sync = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(state =>
{
Log("ThreadPool enter, waiting 250ms...");
sync.WaitOne(250);
Log("ThreadPool exit");
});
Log("Main sleeping 100");
Thread.Sleep(100);
Log("Main about to close");
// sync.Set(); // Is Set called implicitly? No...
sync.Close();
Log("Main waiting for exit 500ms");
Thread.Sleep(500);
}
private static void Log(string text)
{
Console.WriteLine("{0:0} {1}", _timer.ElapsedMilliseconds, text);
}
Когда мы выполняем этот код с Set
звоните прокомментировал, мы получаем это..
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
103 Main about to close
103 Main waiting for exit 500ms
259 ThreadPool exit
Когда мы явно звоним Set
мы получаем это..
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
98 Main about to close
98 ThreadPool exit
98 Main waiting for exit 500ms
Close
- это средство удаления объекта ( Close
и Dispose
в этом классе yield идентичное поведение). На состояние ручки это не влияет. Предполагать, что во всех случаях пользователь захочет, чтобы поток ждал дескриптора, который я закрыл для продолжения, не представляется разумным. Фактически, тот факт, что дескриптор используется , должен указывать на то, что вы не должны в первую очередь вызывать Close
.
Это не вопрос «почему не следует неявно вызывать Set
?», Это концептуальная проблема: если вы звоните Close
, , вы не должны дольше заботиться об объекте . Используйте Set
и Reset
для управления потоком выполнения между потоками; не вызывайте Close
(или Dispose
) для любого объекта, включая WaitHandle
, пока они не перестанут использоваться.
Эти события синхронизации основаны на дескрипторах ожидания Win32, а метод Close ()
только освобождает их (например, Dispose ()
) без сигнализации, и ожидающие потоки продолжают ждать.