2016-06-01 10 views
11

Schreiben Sie eine Antwort für another question einige interessante Dinge kamen heraus und jetzt kann ich nicht verstehen, wie Interlocked.Increment(ref long value) auf 32-Bit-Systemen funktioniert. Lassen Sie mich erklären.Atomic Inkrement von 64 Bit Variable auf 32 Bit Umgebung

Mutter InterlockedIncrement64 jetzt nicht verfügbar ist, wenn für 32-Bit-Umgebung kompiliert, OK, ist es sinnvoll, da in .NET können Sie nicht Speicher auszurichten, wie erforderlich und es kann aufgerufen werden, von verwalten dann ließ sie es.

In .NET wir Interlocked.Increment() mit einer Referenz auf einen 64-Bit-Variable nennen können, wir haben noch keine Einschränkung über seine Ausrichtung (zum Beispiel in einer Struktur, wo wir auch FieldOffset und StructLayout verwenden können), sondern Dokumentation doesn Erwähnen Sie keine Einschränkung (AFAIK). Es ist Magie, es funktioniert!

Hans Passant bemerkt, dass Interlocked.Increment() ist ein spezielle Methode von JIT-Compiler erkannt und es wird einen Aufruf an COMInterlocked::ExchangeAdd64() aussenden, die dann FastInterlockExchangeAddLong nennen, die ein Makro für InterlockedExchangeAdd64 ist die gleiche Einschränkungen von InterlockedIncrement64 teilt.

Jetzt bin ich perplex.

Vergessen Sie für eine Sekunde verwaltete Umgebung und gehen Sie zurück zu nativen. Warum kann InterlockedIncrement64 nicht funktionieren aber InterlockedExchangeAdd64 funktioniert? InterlockedIncrement64 ist ein Makro, wenn intrinsics nicht verfügbar sind und InterlockedExchangeAdd64 Werke dann kann es als Aufruf an InterlockedExchangeAdd64 implementiert werden ...

Lasst uns verwalteten zurück: wie ein Atom-64-Bit-Zuwachs auf 32-Bit-Systemen umgesetzt wird? Ich nehme an Satz "Diese Funktion ist atomisch in Bezug auf Aufrufe zu anderen verriegelten Funktionen" ist wichtig, aber immer noch sah ich keinen Code (danke Hans, um auf tiefere Implementierung hinweisen), es zu tun. Lassen Sie uns InterlockedExchangedAdd64 Implementierung von winbase.h holen, wenn intrinsics nicht verfügbar sind:

FORCEINLINE 
LONGLONG 
InterlockedExchangeAdd64(
    _Inout_ LONGLONG volatile *Addend, 
    _In_ LONGLONG Value 
    ) 
{ 
    LONGLONG Old; 

    do { 
     Old = *Addend; 
    } while (InterlockedCompareExchange64(Addend, 
              Old + Value, 
              Old) != Old); 

    return Old; 
} 

Wie kann es zum Lesen/Schreiben atomar sein?

+0

Wer sagte, dass "InterlockedIncrement64" kann nicht funktionieren, aber "InterlockedExchangeAdd64" tut "? Ihre ursprüngliche Antwort war korrekt, wenn Sie sagen, dass verwalteter Code die systemeigenen Win32-APIs nicht direkt aufrufen kann und erwarten, dass alles funktioniert. Keiner von ihnen wird arbeiten. Sie müssen den verwalteten Helfer verwenden. Die Implementierung des verwalteten Helfers ist jetzt systemeigener Code und ruft die native Funktion auf. Da die Makros und Intrinsics zur Kompilierungszeit aufgelöst werden, zählt die Bitterkeit der CLR. –

+0

Ja, aber 32-Bit-JIT wird InterlockedExchangeAdd64 aufrufen, das dieselben Einschränkungen (in nativ) wie InterlockedIncrement64 aufweist. Was ich nicht verstanden habe, ist, wie es gemacht werden kann (wegen der Speicherausrichtung, wenn nach verwaltetem Code aufgerufen wird). Implementierung auf 32-Bit verwendet InterlockedCompareExchange64, die ... hmmm .... möglicherweise nicht atomare (zum Schreiben von Ergebnis zurück ...) –

+1

* "Wie kann es atomare zum Lesen/Schreiben sein?" * Die Dokumentation für 'InterlockedExchangeAdd64' Hinweise "* Diese Funktion generiert eine vollständige Speicherbarriere (oder einen Zaun), um sicherzustellen, dass die Speichervorgänge der Reihe nach ausgeführt werden." * Beachten Sie, dass die oben gezeigte Implementierung "InterlockedCompareExchange64" aufruft. Bei 32-Bit-Builds gibt dies eine CMPXCHG8B-Anweisung mit einem LOCK-Präfix aus. Dies stellt sicher, dass der Befehl atomar ausgeführt wird. Sie erhalten niemals einen gesperrten Lesevorgang ohne Schreibsperre, daher ist das Schreiben des Ziels atomar. –

Antwort

9

Sie müssen dem Pfad folgen, InterlockedExchangeAdd64() führt Sie zur WinNt.h SDK-Headerdatei. Wo Sie viele Versionen davon sehen werden, abhängig von der Zielarchitektur.

Dies kollabiert im Allgemeinen:

#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64 

, die den Dollar zu einem intrinsischen Compiler übergibt, in vc erklärt/include/intrin.h und vom Compiler Back-End implementiert.

Oder mit anderen Worten, verschiedene Builds der CLR werden verschiedene Implementierungen davon haben. Es gab viele im Laufe der Jahre, x86, x64, Itanium, ARM, ARM8, PowerPC von der Spitze meines Kopfes, ich vermisse sicherlich einige, die WindowsCE zu booten, bevor Apple es irrelevant gemacht. Für x86 wird dies letztendlich durch LOCK CMPXCHNG8B erledigt, eine dedizierte Prozessoranweisung, die mit fehlausgerichteten 64-Bit-Variablen umgehen kann. Ich habe nicht die Hardware, um zu sehen, wie es auf anderen 32-Bit-Prozessoren aussieht.

Bedenken Sie, dass die Zielarchitektur für verwalteten Code nicht zur Kompilierungszeit festgelegt wird. Es ist der Jitter, der die MSIL zur Laufzeit an das Ziel anpasst.Das ist für C++/CLI-Projekte nicht ganz so relevant, da Sie normalerweise ein Ziel auswählen müssen, wenn Sie mit/clr statt/clr: pure kompilieren und nur x86 und x64 funktionieren. Aber die Installation ist sowieso vorhanden, also ist ein Makro einfach nicht sehr nützlich.

+0

Sorry, ich verstehe es nicht. Angenommen, die Implementierung von InterlockedExchangeAdd64 funktioniert gut auf 32-Bit (ohne Instrinsic!), Dann kann auch InterlockedIncrement64-Makro in der gleichen Weise (in 32-Bit-verwalteten Umgebung) implementiert werden. Die Erweiterung dieser dann auch nativen Funktionen auf 64 Bit kann zuverlässig auf 32 Bit implementiert werden. Wie?! Nun, ich denke, diese Formulierung _ "Diese Funktion ist atomar ** in Bezug auf Anrufe zu anderen verzahnten Funktionen **." _ Ist der Schlüssel, aber nach dem Pfad kann ich keinen _Special_ Code sehen, um es zu tun –

+0

Nicht so sicher, wo Das Auflegen lügt, Makros funktionieren nur zur Kompilierzeit. Wenn der Code in der CLR enthalten ist, haben Sie zur Laufzeit eine entsprechende Version, die der Zielarchitektur entspricht. Ein Aufruf in die Helferfunktion bringt also immer die Aufgabe. Es ist nicht gut zur Kompilierzeit in Ihrer eigenen .NET-Assembly, da es auf verschiedenen Plattformen laufen kann. Wenn es zur Laufzeit noch in Inline-Assembly-Code umgewandelt wird (LOCK XADD), wenn es die Zielplattform erlaubt, ist das natürlich der Sweet Spot. In beiden Fällen gibt der Prozessor die Garantie. –

+0

Ja, natürlich verstehe ich, Makros müssen zur Kompilierzeit aufgelöst werden. Angenommen, Sie haben ein gemischtes C++/CLI-Projekt, das auf Win32 abzielt. Sie können Interlocked :: Increment (long long) aufrufen. Zur Laufzeit wird JIT diesen Aufruf durch eine eigene Hilfsfunktion ersetzen. Jetzt: 1) Wenn diese Hilfsfunktion existiert, dann kann auch InterlockedIncrement64 auf die gleiche Weise implementiert werden (anstatt fallen gelassen zu werden). 2) Wie kann es atomar sein, wenn a) Sie auf einem 32-Bit-System sind und b) der Speicher nicht ausgerichtet werden muss? –

Verwandte Themen