2010-04-06 6 views
6

Wir waren überrascht, heute zu erfahren, dass Threads, die auf einen ManualResetEvent warten, auf das Ereignis warten, selbst wenn es geschlossen ist. Wir hätten erwartet, dass der Aufruf von Close() implizit die wartenden Threads signalisiert.Warum warten Threads, die auf ein ManualResetEvent warten, auch wenn Close() aufgerufen wird?

Wir haben dies als Grund dafür festgestellt, dass einige unserer Windows-Dienste nicht so schnell heruntergefahren wurden, wie wir es gerne hätten. Wir ändern alle unsere Dispose Implementierungen, die ManualResetEvent Referenzen schließen, zuerst Set aufrufen.

Kann jemand erklären, warum Close nicht implizit Set aufrufen? Wann möchten Sie, dass ein wartender Thread weiter wartet?

Hier ist unser Testcode unsere Ergebnisse zu zeigen:

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); 
    } 

Wenn wir diesen Code ausführen mit der Set Anruf kommentierte wir diese bekommen ..

0 Main sleeping 100 
0 ThreadPool enter, waiting 250ms... 
103 Main about to close 
103 Main waiting for exit 500ms 
259 ThreadPool exit 

Wenn wir ausdrücklich Set nennen wir bekommen dies ..

0 Main sleeping 100 
0 ThreadPool enter, waiting 250ms... 
98 Main about to close 
98 ThreadPool exit 
98 Main waiting for exit 500ms 

Antwort

2

Close ist ein Mittel von disp Ausstrahlung des Objekts (Close und Dispose auf dieser Klasse ergeben identisches Verhalten). Es hat keinen Einfluss auf den Status des Handles. Anzunehmen, dass der Benutzer in allen Fällen einen Thread wünschen würde, der auf einen Handle wartet, den ich geschlossen habe, scheint nicht sinnvoll zu sein. In der Tat sollte die Tatsache, dass das Handle in Verwendung ist, sollte angeben, dass Sie an erster Stelle nicht Close aufrufen sollten.

Es ist nicht eine Frage des „Warum sollte Set nicht implizit genannt werden?“, Es ist ein konzeptionelles Problem: Wenn Sie Close anrufen, Sie sollten nicht mehr kümmern uns um das Objekt. Verwenden Sie Set und Reset, um den Ausführungsablauf zwischen Threads zu steuern; Rufen Sie nicht Close (oder Dispose) auf einem beliebigen Objekt, WaitHandle s, auf, bis sie nicht mehr verwendet werden.

+0

Um das hinzuzufügen, würde es schwierig sein, Race-Bedingungen zu verfolgen, selbst im Beispiel des OP, würden Sie in Schwierigkeiten geraten, wenn die sync.Close(); zufällig aufgerufen, bevor der Thread-Pool herumgekommen ist, um sync.WaitOne() zu tun; – nos

+0

Das Problem, das ich mit dieser ganzen Sache habe, ist, dass ein WaitHandle entworfen ist, um die Synchronisation zwischen Threads zu unterstützen, also fühle ich mich wie Set on Close und Event Wait after Close sollte implizit behandelt werden. Keine Sorge, wir haben eine benutzerdefinierte erweiterte Klasse erstellt, die die gewünschte Funktionalität hinzufügt. –

+0

@Sam: Ich bin froh, dass Sie eine Lösung gefunden haben, aber das Problem ist, dass Ihre Philosophie über die Lebensdauer des Objekts nicht mit der der Designer übereinstimmt: die bloße Tatsache, dass Sie sich darum kümmern, in welchem ​​Zustand sich der Griff befindet bedeutet, dass Sie aufgrund ihres Designs noch nicht bereit sind, 'Schließen' zu nennen. –

1

Diese Synchronisationsereignisse basieren auf Win32-Wait-Handles, und die Methode Close() gibt sie nur frei (wie Dispose()), ohne zu signalisieren, und wartende Threads warten weiter.