2014-04-16 14 views
8

Dokładniej, potrzebuję tylko zwiększyć podwójność o kolejne podwójne i chcę, aby był bezpieczny dla wątków. Nie chcę używać do tego muteksa, ponieważ prędkość wykonywania dramatycznie spadłaby.Jak wykonać podstawowe operacje z std :: atomic, gdy typ nie jest integralny?

+0

related: [Atomowej podwójne pływający punkt lub ładowanie/przechowywanie wektorów SSE/AVX na x86_64] (https://stackoverflow.com/questions/45055402/atomic-double-floating-point-or-sse-avx-vector-load-store-on-x86-64). Zasadniczo ta sama odpowiedź, ale z szczegółami asm x86. (Niektóre kompilatory dość nieefektywnie pobierają dane z XMM na liczbę całkowitą dla 'compare_exchange' lub nawet ładują/przechowują z' atomowym ', niestety.) –

Odpowiedz

14

Z reguły biblioteka standardowa C++ próbuje zapewnić tylko te operacje, które można skutecznie wdrożyć. W przypadku std::atomic oznacza to operacje, które można wykonywać bez blokady w instrukcji lub dwóch na "typowych" architekturach. "Wspólne" architektury mają instrukcje atomowego pobierania i dodawania dla liczb całkowitych, ale nie dla typów zmiennoprzecinkowych.

Jeśli chcesz realizować operacje matematyczne dla atomowych pływających rodzajów punktów, będziesz musiał zrobić sobie z CAS (porównaj i swap) pętla (Live at Coliru):

std::atomic<double> foo{0}; 

void add_to_foo(double bar) { 
    auto current = foo.load(); 
    while (!foo.compare_exchange_weak(current, current + bar)) 
    ; 
} 
+0

Dziękuję za odpowiedź, nie zdawałem sobie sprawy, że aby to zrobić (dodam dwa podwójne wątki bezpiecznie) potrzebowałbym takiego brudnego (a przynajmniej wygląda na to) obejścia. – Noozen

+0

Jestem trochę zdezorientowany. Dlaczego ten kod nie wejdzie w nieskończoną pętlę, jeśli foo! = Current z powodu modyfikowania foo przez inny wątek między ładunkiem a CAS? – Joe

+1

@Joe 'compare_exchange_weak' pobiera swój pierwszy argument przez odniesienie i aktualizuje go do obserwowanej wartości w przypadku niepowodzenia. Więc jeśli CAS nie powiedzie się becayse 'foo! = Current' pętla oblicza nowy' current + bar' ze zaktualizowanego 'current' i próbuje ponownie. – Casey