Почему потоки, ожидающие на ManualResetEvent, продолжают ожидать, даже когда вызывается метод Close ()? - PullRequest
6 голосов
/ 06 апреля 2010

Мы были удивлены, узнав сегодня, что потоки, ожидающие на 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

Ответы [ 2 ]

2 голосов
/ 06 апреля 2010

Close - это средство избавления от объекта (Close и Dispose в этом классе дают идентичное поведение). Это не влияет на состояние ручки. Предполагать, что во всех случаях пользователь хочет, чтобы поток ожидал обработчик, который я закрыл, чтобы продолжить, не представляется разумным. Фактически, тот факт, что дескриптор используется , должен указывать на то, что вам не следует сначала звонить Close.

Дело не в том, «почему бы не Set не вызываться неявно?», Это концептуальная проблема: если вы звоните Close, , вам больше не нужен объект, Используйте Set и Reset для управления потоком выполнения между потоками; не вызывайте Close (или Dispose) ни для одного объекта, включая WaitHandle s, пока они больше не используются.

1 голос
/ 06 апреля 2010

Эти события синхронизации основаны на дескрипторах ожидания Win32, и метод Close() только освобождает их (например, Dispose()) без сигнализации, и ожидающие потоки продолжают ждать.

...