Laden...

Interlocked modify & threadsichere events

Erstellt von Floste vor 14 Jahren Letzter Beitrag vor 12 Jahren 7.979 Views
Floste Themenstarter:in
1.130 Beiträge seit 2007
vor 14 Jahren
Interlocked modify & threadsichere events

Nachdem ich einen gewissen Webcast von Golo Roden gesehen habe, in dem Lock für threadsichere Events benutzt wurde, habe ich mir sofort gedacht: das ist doch ein klarer Fall für Interlocked.CompareExchange! Das erspart einem auch sämtliche Lockobjekte.

Damit man nicht jedes Mal soviel Schreibaufwand hat, habe ich mir erstmal eine Methode geschrieben, die threadsichere Modifikationen ohne lock erlaubt, solange das Ursprungsobjekt dabei nicht verändert, sondern durch eine ein Neues ausgetaust wird. Also besonders geeignet für: Werttypen, Strings, Delegaten und alle anderen immutablen Referenztypen.


        //Für alle Referenztypen
        public static T InterlockedModify<T>(ref T value, Func<T, T> action) where T : class
        {
            T oldValue;
            T newValue;
            do
            {
                oldValue = value;
                newValue = action(oldValue);
            }
            while (Interlocked.CompareExchange(ref value, newValue, oldValue) != oldValue);
            return newValue;
        }

        //Für Werttyp int. Wer andere Werttypen will, muss sich eine eigene Überladung machen.
        public static int InterlockedModify(ref int value, Func<int, int> action)
        {
            int oldValue;
            int newValue;
            do
            {
                oldValue = value;
                newValue = action(oldValue);
            }
            while (Interlocked.CompareExchange(ref value, newValue, oldValue) != oldValue);
            return newValue;
        }

Man kann mit dieser Methode z.B. Threadsicher
-Mathematische Operationen ausführen:

InterlockedModify(ref wert, (v)=> v*3 );

-Strings oder Delegaten Verketten:

InterlockedModify(ref wert, (v)=> v+" hallo" );

Wichtig ist, dass keine Eigenschaften von Referenztypen geändert werden (jedenfalls nicht die vom Original) und die übergebene Methode (der 2. Parameter) mehrmals ausgeführt werden kann, wobei alle Ergebnisse bis auf das des letzten Aufrufs verworfen werden.

Hier also der Code für Threadsichere Events ohne lock:


        private EventHandler myEvent;

        public event EventHandler MyEvent
        {
            add
            {
                InterlockedModify(ref myEvent, ((v) => v + value));
            }
            remove
            {
                InterlockedModify(ref myEvent, ((v) => v - value));
            }
        }

        protected virtual void OnMyEvent()
        {
            EventHandler handler=myEvent;
            if (handler != null)
            {
                handler(this, null);
            }
        }

Die Compilergenerierten Events benutzen ([MethodImpl(MethodImplOptions.Synchronized)]

was einem lock(this){....} entspricht

Siehe auch:
Lockfreie threadsichere Queue
Lockfreier threadsicherer Stack

Schlagwörter:

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

4.207 Beiträge seit 2003
vor 14 Jahren

Hmmm ...

Ich habe jetzt eine ganze Weile überlegt, was ich von dem Code halte. Und entweder habe ich ihn nicht verstanden, oder er ist tatsächlich ein wenig merkwürdig 😉.

Was mich irritiert, ist die while-Schleife ... Du ersetzt letztlich value durch newValue, falls value = oldValue gilt. Das gilt im ersten Schleifendurchlauf aber in jedem Fall, da oldValue ja gerade so definiert ist, dass es = value ist.

In der while-Bedingung prüfst Du dann, ob der Rückgabewert != oldValue ist. Das kann im ersten Durchlauf aber nie der Fall sein, weil der Rückgabewert immer dem ursprünglichen Wert entspricht (also oldValue), egal ob die Ersetzung durchgeführt wurde oder nicht.

Das heißt doch, die Schleife wird mindestens ein zweites Mal betreten, und das ist ja dann - gerade im Hinblick darauf, dass die übergebene Methode mindestens zwei Mal ausgeführt wird - auch nicht mehr so wahnsinnig performant. Oder habe ich da irgendetwas übersehen?

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

3.971 Beiträge seit 2006
vor 14 Jahren

Nachdem ich einen gewissen Webcast von Golo Roden gesehen habe, in dem Lock für threadsichere Events benutzt wurde

Hallo Floste,
wäre nett, wenn du den entsprechenden Link noch posten würdest.

Weiterhin hab ich nicht verstanden, warum du die Funktion zweimal implementierst (doppelter Code)


public static T InterlockedModify<T>(ref T value, Func<T, T> action) where T : class
public static int InterlockedModify(ref int value, Func<int, int> action)

Beide Funktionen haben augenscheinlich die selbe Implementierung. Lass am besten die zweite Implementierung weg und entferne bei der ersten die Einschränkung auf class

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

4.207 Beiträge seit 2003
vor 14 Jahren
  1. Siehe Events und Delegates im Detail - Feinheiten in MSIL

  2. Weil die InterlockedExchange-Methode einmal für Referenztypen existiert, und einmal für diverse Wertetypen, und man die Variante für Referenztypen nicht mit Wertetypen verwenden soll.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

1.361 Beiträge seit 2007
vor 14 Jahren

Hi,

[...]Das kann im ersten Durchlauf aber nie der Fall sein [...] Das heißt doch, die Schleife wird mindestens ein zweites Mal betreten.

Logikfehler 😉 Deine Schlussfolgerung müsste wenn dann sein, dass die schleife nie ein zweites mal durchlaufen wird.

Also wenn man so analysiert wie du, wird die Schleife genau einmal durchlaufen, die Berechnung einmal durchgeführt und der Wert einmal ersetzt. Fertig.
Genau so soll es auch sein (wenn man single-threaded analysiert)

Aber jetzt stell dir vor, diese Methode wird zeitgleich von mehreren Threads ausgeführt. Das kann ja passieren - gerade weil kein Lock vorhanden ist. Wenn jetzt der erste Thread direkt nach seinem

oldValue = value;

unterbrochen wird, nun der zweite Thread kommt, selbst ne Berechnung durchführt und value ersetzt, dann hat der erste Thread leider mit dem falschen Wert die Berechnung durchgeführt.
Das merkt er aber dank der Compare-Exchange-Methode und führt einfach seine Berechnung nochmal auf dem neuen Wert (Ergebnis von Thread 2) durch.

Im Prinzip haben wir hier eine Race Condition. Nur merken die Threads dank der atomaren CompareExchange, ob sie erster oder zweiter sind und können darauf reagieren.

Vorteil der Implementierung ist eben, dass man sich um kein lock kümmern muss und dass der Overhead für den Lock wegfällt. Und im Normalfall sind auch die Threads nicht gleichzeitig in der Funktion. (also zu 99% wird die Schleife einmal durchlaufen, genau wie ein lock zu 99% auch einfach klappt ohne zu warten)

@ Floste
Find die Implementierung super. 👍 Die Interlocked Methoden werden von "Endnutzer"-Programmieren viel zu selten eingesetzt. Dabe sind die sooo schön 🙂

beste Grüße
zommi

4.207 Beiträge seit 2003
vor 14 Jahren

Du hast natürlich recht, danke für die Erklärung! War wohl noch zu früh heute Morgen 😉.

Wenn man es mal von dieser Warte sieht, ist der Code eigentlich sehr elegant, vor allem ist schön, dass es halt um einiges performanter ist als mit einem Lock, weil erstens in 99% der Fälle kein Lock gebraucht würde, und zweitens die Interlocked-Methoden ohnehin schneller sind als ein Lock.

Insofern: Sehr schön!

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

5.742 Beiträge seit 2007
vor 12 Jahren

Hallo zusammen,

als kleine Ergänzung:

Die Compilergenerierten Events benutzen [MethodImpl(MethodImplOptions.Synchronized)]
was einem lock(this){....} entspricht

Dies trifft ab C# 4.0 nicht mehr zu - die scheinen Flostes Implementierung übernommen zu haben 😉
Siehe Events get a little overhaul in C# 4, Part I: Locks

Floste Themenstarter:in
1.130 Beiträge seit 2007
vor 12 Jahren

😁 Haha, wurde auch langsam zeit....

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!