Czym dokładnie jest std:: atomic?

Rozumiem, że std::atomic<> jest obiektem atomowym. Ale atomowy w jakim stopniu? Według mnie operacja może być atomowa. Co dokładnie oznacza uczynienie obiektu atomowym? Na przykład, jeśli istnieją dwa wątki jednocześnie wykonujące następujący kod:

a = a + 12;

To czy cała operacja (powiedzmy add_twelve_to(int)) jest atomowa? Czy zmiany w zmiennej atomic (so operator=())?

Author: Azeem, 2015-08-13

2 answers

Każda instancja i pełna specjalizacja std::atomic reprezentuje typ, na którym różne wątki mogą jednocześnie działać (ich instancje), bez podnoszenia niezdefiniowanego zachowania:

Obiekty typów atomowych są jedynymi obiektami C++, które są wolne od Ras danych; to znaczy, jeśli jeden wątek pisze do obiektu atomowego, podczas gdy inny wątek czyta z niego, zachowanie jest dobrze zdefiniowane.

Ponadto dostęp do obiektów atomowych może ustanowić synchronizacja między wątkami i porządkowanie dostępu do pamięci niematomicznych, jak określono w std::memory_order.

std::atomic<> operacje, które w pre-C++ 11 razy musiały być wykonywane przy użyciu (na przykład) funkcji interlocked Z MSVC lub Atomic bultins W przypadku GCC.

Ponadto, std::atomic<> daje większą kontrolę, pozwalając na różne rozkazy pamięci , które określają ograniczenia synchronizacji i porządkowania. Jeśli chcesz poczytać więcej o Atomice i pamięci C++ 11 model, te linki mogą być przydatne:

Zauważ, że w typowych przypadkach użycia prawdopodobnie użyłbyś przeciążonej arytmetyki operatory lub inny ich zbiór :

std::atomic<long> value(0);
value++; //This is an atomic op
value += 5; //And so is this

Ponieważ składnia operatora nie pozwala na określenie kolejności pamięci, operacje te będą wykonywane z std::memory_order_seq_cst, ponieważ jest to domyślna kolejność dla wszystkich operacji atomowych w C++ 11. Gwarantuje sekwencyjną spójność (całkowity porządek globalny) pomiędzy wszystkimi operacjami atomowymi.

W niektórych przypadkach może to nie być wymagane (i nic nie przychodzi za darmo) , więc możesz chcieć użyć bardziej wyraźnego forma:

std::atomic<long> value {0};
value.fetch_add(1, std::memory_order_relaxed); // Atomic, but there are no synchronization or ordering constraints
value.fetch_add(5, std::memory_order_release); // Atomic, performs 'release' operation

Teraz twój przykład:

a = a + 12;

Nie będzie oceniać do pojedynczego atomu op: spowoduje to a.load() (który jest atomowy), a następnie dodanie między tą wartością a 12 i a.store() (również atomowy) wyniku końcowego. Jak już wcześniej wspomniałem, std::memory_order_seq_cst będzie tutaj używany.

Jeśli jednak napiszesz a += 12, będzie to operacja atomowa (jak już wcześniej wspomniałem) i jest mniej więcej równoważna a.fetch_add(12, std::memory_order_seq_cst).

Co do Twojego komentarza:

Zwykły int ma ładunki atomowe i sklepy. Jaki jest sens owijania go atomic<>?

Twoje stwierdzenie jest prawdziwe tylko dla architektur, które zapewniają taką gwarancję atomiczności dla sklepów i / lub obciążeń. Są architektury, które tego nie robią. Ponadto, zwykle wymagane jest, aby operacje były wykonywane na adresach Word- / dword-aligned, aby były atomowe std::atomic<> jest czymś, co jest gwarantowane na na każdej platformie, bez dodatkowych wymagań. Ponadto pozwala na napisz kod w ten sposób:

void* sharedData = nullptr;
std::atomic<int> ready_flag = 0;

// Thread 1
void produce()
{
    sharedData = generateData();
    ready_flag.store(1, std::memory_order_release);
}

// Thread 2
void consume()
{
    while (ready_flag.load(std::memory_order_acquire) == 0)
    {
        std::this_thread::yield();
    }

    assert(sharedData != nullptr); // will never trigger
    processData(sharedData);
}

Zauważ, że warunek twierdzenia zawsze będzie prawdziwy (a więc nigdy się nie uruchomi), więc zawsze możesz być pewien, że dane są gotowe po wyjściu pętli while. Dlatego, że:

  • store() to flaga jest wykonywana po ustawieniu sharedData (Zakładamy, że generateData() zawsze zwraca coś użytecznego, w szczególności nigdy nie zwraca NULL) i używa std::memory_order_release order:

memory_order_release

Operacja sklepu z tą pamięcią wykonuje release operacja: nie można zmienić kolejności odczytów ani zapisów w bieżącym wątku po tym sklepie. wszystkie wpisy w bieżącym wątku są widoczne w inne wątki, które nabywają tę samą zmienną atomową

  • sharedData jest używane po wyjściu pętli while, a zatem po load() from flag zwróci niezerową wartość. load() używastd::memory_order_acquire kolejność:

std::memory_order_acquire

Operacja ładowania z tą pamięcią order wykonuje operację acquire na dotkniętej lokalizacji pamięci: brak odczytów i zapisów w bieżącym wątek może być ponownie uporządkowany przed tym obciążeniem. wszystkie wpisy w innych wątkach które uwalniają tę samą zmienną atomową są widoczne w bieżącym wątek .

Daje Ci to precyzyjną kontrolę nad synchronizacją i pozwala ci wyraźnie określić, jak Twój kod może/może nie/będzie / nie będzie się zachowywał. Nie byłoby to możliwe, gdyby tylko gwarancją była sama atomiczność. Szczególnie jeśli chodzi o bardzo ciekawe modele synchronizacji, takie jak release-consume ordering .

 76
Author: Mateusz Grzejek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-24 15:40:02

Rozumiem, że std::atomic<> sprawia, że obiekt jest atomowy.

To kwestia perspektywy... nie można zastosować go do dowolnych obiektów i ich operacje stają się atomowe, ale można użyć podanych specjalizacji dla (większości) typów całkowych i wskaźników.

a = a + 12;

std::atomic<> nie (używa wyrażeń szablonowych do) uproszczenia tego do pojedynczej operacji atomowej, zamiast tego Element operator T() const volatile noexcept wykonuje atomową load() z a, Następnie dodaje się dwanaście i operator=(T t) noexcept robi store(t).

 12
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-13 02:42:17