2016-11-17 3 views
3

Ich frage mich, ob dies Thread-Safe ist oder (wenn nicht), wie ich es sicher machen kann. Es wird von einem Zeitgeber genannt:Muss ich ein Bool sperren, wenn ich es in zwei aufeinanderfolgenden Anweisungen lese/schreibe?

private volatile bool _isSynchronizing; 

private void SynchronizeSessionCache(object state = null) 
{ 
    if (_isSynchronizing) 
    { 
     Log.Warn($"Aborted synchronization of SessionCache with SessionManager because we are already synchronizing. Interval is: {SynchronizationInterval}"); 
     return; 
    } 

    _isSynchronizing = true; 

    bool lockWasTaken = false; 
    try 
    { 
     // some code that doesn't need a lock ... 
     // ... 

     // lock this part 
     Monitor.Enter(_lockObject, ref lockWasTaken); 
     // main code ... 
    } 
    finally // omitted catch 
    { 
     _isSynchronizing = false; 
     if(lockWasTaken) 
      Monitor.Exit(_lockObject); 
    } 
} 

Meine Sorge ist, dass es, dass ein Thread überprüft _isSynchronizing am Anfang seiner Methode möglich sein könnte, und es ist false zu diesem Zeitpunkt. Dann kommt ein anderer Thread in den Körper, weil er noch nicht auf true von thread1 gesetzt ist. Ist das möglich, auch wenn _isSynchronizingvolatile ist? Wenn ja, wie kann diese Methode threadsicher gemacht werden?

Wenn ich es richtig verstehe volatile kann solche Race-Bedingungen nicht verhindern, sondern stellt nur sicher, dass die Variable nicht zwischengespeichert wird, so dass alle Threads immer den aktuellen Wert lesen.

+0

Ja, es ist möglich, dass beide Laufflächen die erste Prüfung bestehen. Aber dann innerhalb der Methode haben Sie eine Sperre, so dass Sie erwarten, dass mehrere Threads eingeben, also was ist die Idee? – Evk

+0

@Evk: Ich erwarte nicht, dass mehrere Threads eingeben. Das '_lockObject' wird auch in anderen Methoden und Ereignissen verwendet, die Zugriff auf freigegebene Ressourcen benötigen. Dies muss während der Synchronisierung gesperrt werden. Es sollte nie passieren, dass mehrere Threads gleichzeitig eintreffen. Deshalb benutze ich das '_isSynchronizing' -Flag. –

+0

Gut, dann würde ich hier einfach die Sperrung verwenden. – Evk

Antwort

4

Um die Threadsicherheit zu gewährleisten, müssen Sie den Vergleich und die Zuordnung zu einer einzigen atomaren Operation durchführen. Andernfalls besteht immer die Möglichkeit, dass ein anderer Thread den Vergleich bestehen kann, während die Zuweisung vom ersten Thread ausgeführt wird. Das Schlüsselwort volatile hilft hier nicht, da es eher dem Compiler (und der Laufzeit) mitteilt, dass keine Optimierung erlaubt ist, die die Reihenfolge der Lese-Schreib-Operationen, an denen diese Variable beteiligt ist, ändern könnte.

(Mehr über das volatile, was Sie in this großen Artikeln von Eric Lippert lesen können.)

Die einfache (etwas langsamere) Lösung wäre, einen kritischen Abschnitt um den Vergleich zu gründen und die Zuordnung:

Es gibt eine schnellere Lösung, aber nicht für eine Variable bool. Für eine int könnten Sie die Interlocked.CompareExchange() Methode verwenden.

Nehmen wir an, dass int _isSynchronizing = 0 bedeutet false und _isSynchronizing = 1 bedeutet true.

Dann können Sie diese Anweisung verwenden:

if (Interlocked.CompareExchange(ref _isSynchronizing, 1, 0) == 1) 
{ 
    // If the original val == 1, we're already synchronizing 
    return; 
} 

Dies ist etwas schneller als ein Monitor verwenden, aber es gibt keine bool Überlastung.

+0

Für Klarheit in dieser Situation. Wäre korrekt, '' 'Interlocked.Decrement (ref _isSynchronizing);' '' oder eine direkte Zuweisung zu Null ohne Sperren etc – NevilleDastur

Verwandte Themen