2017-11-27 2 views
3

Eventwaithandle Studium Multi-Threading von albahari atricle haben. Benötige ich ein Schloss _locker im folgenden Beispiel? Ich vermute nein, da _message durch EventWaitHandle geschützt ist. Habe ich recht?Muss ich eine Sperre benötigen, wenn ich

class TwoWaySignaling 
{ 
    static EventWaitHandle _ready = new AutoResetEvent (false); 
    static EventWaitHandle _go = new AutoResetEvent (false); 
    static readonly object _locker = new object(); 
    static string _message; 

    static void Main() 
    { 
    new Thread (Work).Start(); 

    _ready.WaitOne();     // First wait until worker is ready 
    lock (_locker) _message = "ooo"; 
    _go.Set();       // Tell worker to go 

    _ready.WaitOne(); 
    lock (_locker) _message = "ahhh"; // Give the worker another message 
    _go.Set(); 
    _ready.WaitOne(); 
    lock (_locker) _message = null; // Signal the worker to exit 
    _go.Set(); 
    } 

    static void Work() 
    { 
    while (true) 
    { 
     _ready.Set();       // Indicate that we're ready 
     _go.WaitOne();       // Wait to be kicked off... 
     lock (_locker) 
     { 
     if (_message == null) return;  // Gracefully exit 
     Console.WriteLine (_message); 
     } 
    } 
    } 
} 
+2

Ich kann nicht sehen, auf welche Weise das Entfernen der 'Sperre' die Reihenfolge der Ausführung in diesem Beispiel ändern könnte. – Rotem

+3

Keine Sperre erforderlich. Das Ping-Pong-Verfahren zwischen den beiden AutoResetEvents stellt die Synchronisierung bereit und stellt sicher, dass die Threads nicht gleichzeitig auf die Variable zugreifen können. Der WaitOne() - Aufruf stellt eine Speicherbarriere bereit, die sicherstellt, dass das Variablenupdate für einen anderen Thread sichtbar ist. Diese Art von zufälligen Barrieren sind nicht sehr schön, aber nicht ungewöhnlich. Sie bevorzugen die Verwendung der Barrier-Klasse, deren SignalAndWait() -Methode diesen Code viel leichter verständlich macht. Und ist viel effizienter. Code, den Sie verstehen können, vermeidet das Einfädeln von Fehlern, effizienter Code macht alle glücklich. –

Antwort

3

Sie haben Recht.

Diese Art von Problemen kann nicht einfach mit Versuch und Irrtum getestet werden. Sie werden am besten mit logischem Denken analysiert:

  • Welcher Thread läuft welcher Code?
  • Was in dem anderen Thread jetzt passieren könnte?
  • Was passiert, wenn der andere Thread mehr CPU-Zeit bekommt? usw.

Der Haupt-Thread nur den Code in Main() ausführen und der Arbeiter-Thread nur den Code in Work() auszuführen. Das ist einfach genug.

Wenn man sich anschaut, wie die kritische Ressource zugegriffen wird Sie, dass der Zugang zu _message in Main() beachten Sie wird immer zwischen

_ready.WaitOne(); 

und

_go.Set(); 

Der Zugang zu _message in Work() immer zwischen

_go.WaitOne(); 
Daher

und

_ready.Set(); 

, einer der Threads wird immer für die anderen warten, bevor _message Zugriff auf und die Sperre ist nicht erforderlich.

0

Sie hielten Ihre Fäden aus der _message Variable zur gleichen Zeit zugreifen. Ihre Logik hier ist autark und vorhersehbar, was nicht immer der Fall ist. Manchmal lesen Sie von anderen Orten und der Fluss ist nicht vollständig kontrollierbar. In diesem Fall müssen Sie kritische Variablen sperren.

Verwandte Themen