2010-03-19 2 views
32

zu stoppen? Mein Hauptanliegen ist das boolesche Flag ... ist es sicher, es ohne Synchronisation zu benutzen? Ich habe an mehreren Stellen gelesen, dass es atomar ist (einschließlich der Dokumentation).Ist es sicher, ein boolesches Flag zu verwenden, um die Ausführung eines Threads in C#

class MyTask 
{ 
    private ManualResetEvent startSignal; 
    private CountDownLatch latch; 
    private bool running; 

    MyTask(CountDownLatch latch) 
    { 
     running = false; 
     this.latch = latch; 
     startSignal = new ManualResetEvent(false); 
    } 

    // A method which runs in a thread 
    public void Run() 
    { 
     startSignal.WaitOne(); 
     while(running) 
     { 
      startSignal.WaitOne(); 
      //... some code 
     } 
     latch.Signal(); 
    } 

    public void Stop() 
    { 
     running = false; 
     startSignal.Set(); 
    } 

    public void Start() 
    { 
     running = true; 
     startSignal.Set(); 
    } 

    public void Pause() 
    { 
     startSignal.Reset(); 
    } 

    public void Resume() 
    { 
     startSignal.Set(); 
    } 
} 

Ist dies ein sicherer Weg, um eine Aufgabe auf diese Weise zu entwerfen? Irgendwelche Vorschläge, Verbesserungen, Kommentare?

Hinweis: Ich schrieb meine benutzerdefinierte CountDownLatch Klasse für den Fall, dass Sie sich fragen, woher ich es bekomme.

Update:
Hier ist meine CountDownLatch auch:

public class CountDownLatch 
{ 
    private volatile int m_remain; 
    private EventWaitHandle m_event; 

    public CountDownLatch (int count) 
    { 
     if (count < 0) 
      throw new ArgumentOutOfRangeException(); 
     m_remain = count; 
     m_event = new ManualResetEvent(false); 
     if (m_remain == 0) 
     { 
      m_event.Set(); 
     } 
    } 

    public void Signal() 
    { 
     // The last thread to signal also sets the event. 
     if (Interlocked.Decrement(ref m_remain) == 0) 
      m_event.Set(); 
    } 

    public void Wait() 
    { 
     m_event.WaitOne(); 
    } 
} 

Antwort

45

Sie besser es volatile obwohl markieren:

Das Schlüsselwort volatile zeigt an, dass ein Feld möglicherweise geändert wurde b y mehrere gleichzeitig Threads ausgeführt. Die Felder , die als flüchtig deklariert sind, sind nicht , abhängig von Compiler-Optimierungen, die Zugriff von einem einzelnen Thread annehmen. Diese stellt sicher, dass immer der aktuellste Wert im Feld vorhanden ist.

Aber ich würde Ihre Schleife ändern:

startSignal.WaitOne(); 
    while(running) 
    { 
     //... some code 
     startSignal.WaitOne(); 
    } 

Wie es in Ihrem Beitrag die ‚einige Code‘ ist möglicherweise ausgeführt wird, wenn der Faden gestoppt ist (dh wenn Stopp aufgerufen wird.), Welche unerwartet und möglicherweise sogar falsch.

+0

@Remus Guter Fang in meiner while-Schleife Code-Reihenfolge. In Bezug auf die volatile Flagge: Macht es wirklich einen Unterschied, ob die Flagge in diesem Fall volatil ist? Wenn es das erste Update vermisst, dann wird es noch einen Durchlauf durch die Schleife machen und es beim nächsten Mal fangen ... – Kiril

+6

Wenn Sie es nicht flüchtig markieren, könnte der generierte Code den Wert in eine Registrierung und Ihre optimieren Thread wird * nie * die Änderung sehen. –

+4

@Remus Ich bekomme es jetzt: die Atomizität hat nichts mit der Sichtbarkeit zwischen Threads zu tun ... nur weil eine Operation in einem CPU-Zyklus ausgeführt wird, bedeutet das nicht, dass das Ergebnis für die anderen Threads sichtbar ist, es sei denn der Wert ist volatil markiert. – Kiril

4

Boolesche Werte sind in C# atomar. Wenn Sie sie jedoch in einem Thread ändern und in einem anderen lesen möchten, müssen Sie sie mindestens als flüchtig markieren. Andernfalls liest der Lese-Thread es tatsächlich nur einmal in ein Register ein.

+0

@Michael Also, wie viele Zyklen durch die Schleife kann realistisch passieren, bevor die Schleife es fängt? Ich nahm an, dass es einer wäre, aber ich nehme an, dass es keine solche Garantie gibt ... – Kiril

+0

@Michael Ich habe es jetzt: Ich habe gerade gemerkt, dass ich Atomarität mit Sichtbarkeit verwechselt habe. – Kiril

0

BTW, ich habe gerade bemerkt, diesen Teil des Codes:

// A method which runs in a thread 
    public void Run() 
    { 
     startSignal.WaitOne(); 
     while(running) 
     { 
      startSignal.WaitOne(); 
      //... some code 
     } 
     latch.Signal(); 
    } 

Sie müssen den Arbeiter-Thread zweimal mit „startSignal.Set()“ für den Code innerhalb des während des Blockes entsperren auszuführen.

Ist das beabsichtigt?

+2

@Akapetronics startSignal muss nur einmal eingestellt werden. WaitOne setzt das Ereignis nicht zurück, daher kann ich startSignal.WaitOne() mehrmals aufrufen und es wird nicht blockiert, bis startSignal.Reset() aufgerufen wird. Der Entwurf ist absichtlich: Der Thread wird blockiert, bis die Start-Methode aufgerufen wird und die Start-Methode setzt zuerst das Running-Flag und setzt das StartSignal, sodass die While-Schleife mit der Ausführung beginnen kann. Wenn ich die Reihenfolge umkehre und zuerst das startSignal setze, könnte die while-Schleife enden, bevor ich überhaupt das running-Flag gesetzt habe. Beachten Sie, dass dieses Design die Notwendigkeit von Sperren beseitigt. – Kiril

Verwandte Themen