2013-07-29 19 views
9

Es gibt zwei atomare CAS-Operationen in C++ 11: atomic_compare_exchange_weak und atomic_compare_exchange_strong.Wann sollte std :: atomic_compare_exchange_strong verwendet werden?

Nach cppreference:

Die schwachen Formen der Funktionen erlaubt spuriously scheitern, dass ist, handeln, als ob * obj = * erwartet, auch wenn sie gleich sind!. Wenn ein Compare-and-Exchange in einer Schleife ist, wird die schwache Version eine bessere Leistung auf einigen Plattformen liefern. Wenn ein schwaches Vergleichen und Tausch eine Schleife erfordern würde und eine starke würde nicht, ist die starke vorzuziehen.

Das Folgende ist ein Beispiel für die Verwendung der schwache Version, denke ich:

do { 
    expected = current.value(); 
    desired = f(expected); 
} while (!current.atomic_compare_exchange_weak(expected, desired)); 

Könnte jemand ein Beispiel geben, wo der Vergleichs- und Austausch nicht in einer Schleife ist, so dass die stark Version ist vorzuziehen?

+0

Es scheint, als könnte dies auftreten, wenn die Operation nicht kritisch war. Nehmen wir an, Sie haben eine Reihe von Worker-Threads, die regelmäßig arbeiten, aber sie prüfen auch, ob in einer Warteschlange zusätzliche Arbeiten ausgeführt werden. Wenn die CAS-Operation den Eindruck erweckt, dass die Warteschlange nicht gesperrt werden kann, ist dies möglicherweise keine große Sache, da der Mitarbeiter seine normale Arbeit fortsetzen kann. –

Antwort

9

Die atomic_compare_exchange_XXX Funktionen aktualisieren ihre „erwartete“ Argument mit dem Wert beobachtet, so dass Ihre Schleife die gleiche ist, wie:

expected = current; 
do { 
    desired = f(expected); 
} while (!current.atomic_compare_exchange_weak(expected, desired)); 

Wenn der gewünschte Wert unabhängig von dem erwarteten Wert ist, diese Schleife wird:

desired = ...; 
expected = current; 
while (current.atomic_compare_exchange_weak(expected, desired)) 
    ; 

Lassen Sie uns einige Semantik hinzufügen. Nehmen wir an, dass mehrere Threads dies gleichzeitig ausführen. In jedem Fall desired ist eine ID ungleich 0 für den aktuellen Thread, und current wird verwendet, um gegenseitigen Ausschluss bereitzustellen, um sicherzustellen, dass einige Thread eine Bereinigungsaufgabe ausführt. Uns ist es egal, aber wir wollen sicher sein, dass irgendein Thread Zugriff erhält (und vielleicht können andere Threads den Gewinner beobachten, indem sie seine ID aus current lesen).

Wir können die gewünschte Semantik mit erreichen:

expected = 0; 
if (current.atomic_compare_exchange_strong(expected, this_thread)) { 
    // I'm the winner 
    do_some_cleanup_thing(); 
    current = 0; 
} else { 
    std::cout << expected << " is the winner\n"; 
} 

Dies ist ein Fall, in dem atomic_compare_exchange_weak eine Schleife erfordern würde, die gleiche Wirkung wie atomic_compare_exchange_strong, da störende Ausfälle sind möglich zu erreichen:

expected = 0; 
while(!current.atomic_compare_exchange_weak(expected, this_thread) 
     && expected == 0)) 
    ; 
if (expected == this_thread) { 
    do_some_cleanup_thing(); 
    current = 0; 
} else { 
    std::cout << expected << " is the winner\n"; 
} 

Der Standard schlägt vor, dass Implementierungen in diesem Fall einen effizienteren Code für atomic_compare_exchange_strong bereitstellen als eine Schleife mit ..._weak (§29.6.5/25 [atomics.types.operations.req]).

Verwandte Themen