Namespaces
Variants

Atomic types

From cppreference.net

Inhaltsverzeichnis

Syntax

_Atomic ( Typname ) (1) (seit C11)
_Atomic Typname (2) (seit C11)
1) Als Typbezeichner verwenden; dies kennzeichnet einen neuen atomaren Typ
2) Verwendung als Typqualifizierer; dies bezeichnet die atomare Version von type-name . In dieser Rolle kann es mit const , volatile , und restrict kombiniert werden, obwohl im Gegensatz zu anderen Qualifizierern die atomare Version von type-name eine andere Größe, Ausrichtung und Objektdarstellung haben kann.
type-name - jeder Typ außer Array oder Funktion. Für (1) darf type-name ebenfalls nicht atomic oder cvr-qualifiziert sein

Der Header <stdatomic.h> definiert viele praktische Typaliase , von atomic_bool bis atomic_uintmax_t , die die Verwendung dieses Schlüsselworts mit eingebauten und Bibliothekstypen vereinfachen.

_Atomic const int* p1;  // p ist ein Zeiger auf ein atomares const int
const atomic_int* p2;   // dasselbe
const _Atomic(int)* p3; // dasselbe

Falls die Makrokonstante __STDC_NO_ATOMICS__ vom Compiler definiert wird, ist das Schlüsselwort _Atomic nicht verfügbar.

Erklärung

Objekte atomarer Typen sind die einzigen Objekte, die frei von Datenrennen sind; das heißt, sie können von zwei Threads gleichzeitig geändert oder von einem geändert und von einem anderen gelesen werden.

Jedes atomare Objekt hat seine eigene zugehörige Modifikationsreihenfolge , die eine totale Ordnung der an diesem Objekt vorgenommenen Modifikationen darstellt. Wenn aus der Sicht eines Threads die Modifikation A eines atomaren M happens-before der Modifikation B desselben atomaren M , dann tritt in der Modifikationsreihenfolge von M , A vor B auf.

Beachten Sie, dass zwar jedes atomare Objekt seine eigene Modifikationsreihenfolge hat, es jedoch keine einzige Gesamtreihenfolge gibt; verschiedene Threads können Modifikationen an verschiedenen atomaren Objekten in unterschiedlicher Reihenfolge beobachten.

Es gibt vier Kohärenzarten, die für alle atomaren Operationen garantiert werden:

  1. Write-Write-Kohärenz : Wenn eine Operation A , die ein atomares Objekt M modifiziert, vor einer Operation B stattfindet, die M modifiziert, dann erscheint A vor B in der Modifikationsreihenfolge von M .
  2. Read-Read-Kohärenz : Wenn eine Werteberechnung A eines atomaren Objekts M vor einer Werteberechnung B von M stattfindet und A seinen Wert von einem Nebeneffekt X auf M bezieht, dann ist der von B berechnete Wert entweder der von X gespeicherte Wert oder der von einem Nebeneffekt Y auf M gespeicherte Wert, wobei Y nach X in der Modifikationsreihenfolge von M erscheint.
  3. Read-Write-Kohärenz : Wenn eine Werteberechnung A eines atomaren Objekts M vor einer Operation B auf M stattfindet, dann bezieht A seinen Wert von einem Nebeneffekt X auf M , wobei X vor B in der Modifikationsreihenfolge von M erscheint.
  4. Write-Read-Kohärenz : Wenn ein Nebeneffekt X auf ein atomares Objekt M vor einer Werteberechnung B von M stattfindet, dann bezieht die Auswertung B ihren Wert von X oder von einem Nebeneffekt Y , der nach X in der Modifikationsreihenfolge von M erscheint.

Einige atomare Operationen sind auch Synchronisationsoperationen; sie können zusätzliche Release-Semantik, Acquire-Semantik oder sequentiell-konsistente Semantik haben. Siehe memory_order .

Integrierte Inkrement- und Dekrementoperatoren und zusammengesetzte Zuweisungen sind atomare Lese-Modifizieren-Schreibe-Operationen mit vollständig sequentiell konsistenter Ordnung (als ob memory_order_seq_cst verwendet würde). Falls weniger strikte Synchronisationssemantik gewünscht ist, können stattdessen die Standardbibliotheksfunktionen verwendet werden.

Atomare Eigenschaften sind nur für Lvalue-Ausdrücke von Bedeutung. Die Lvalue-zu-Rvalue-Konvertierung (die einen Speicherlesevorgang von einem atomaren Ort in eine CPU-Register modelliert) entfernt die Atomarität zusammen mit anderen Qualifizierern.

Hinweise

Der Zugriff auf ein Mitglied einer atomaren Struktur/Union ist undefiniertes Verhalten.

Der Bibliothekstyp sig_atomic_t bietet keine Thread-Synchronisation oder Speicherreihenfolge, lediglich Atomarität.

Die volatile -Typen bieten keine Thread-Synchronisation, Speicherreihenfolge oder Atomarität.

Es wird empfohlen, dass Implementierungen sicherstellen, dass die Darstellung von _Atomic ( T ) in C identisch ist mit der von std :: atomic < T > in C++ für jeden möglichen Typ T . Die verwendeten Mechanismen zur Sicherstellung von Atomarität und Speicherreihenfolge sollten kompatibel sein.

Schlüsselwörter

_Atomic

Beispiel

#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
atomic_int acnt;
int cnt;
int f(void* thr_data)
{
    for (int n = 0; n < 1000; ++n)
    {
        ++cnt;
        ++acnt;
        // for this example, relaxed memory order is sufficient, e.g.
        // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
    }
    return 0;
}
int main(void)
{
    thrd_t thr[10];
    for (int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for (int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);
    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

Mögliche Ausgabe:

The atomic counter is 10000
The non-atomic counter is 8644

Referenzen

  • C23 Standard (ISO/IEC 9899:2024):
  • 6.7.2.4 Atomic type specifiers (S.: TBD)
  • 7.17 Atomics <stdatomic.h> (S.: TBD)
  • C17-Standard (ISO/IEC 9899:2018):
  • 6.7.2.4 Atomare Typbezeichner (S: 87)
  • 7.17 Atomics <stdatomic.h> (S: 200-209)
  • C11-Standard (ISO/IEC 9899:2011):
  • 6.7.2.4 Atomic type specifiers (S. 121)
  • 7.17 Atomics <stdatomic.h> (S. 273-286)

Siehe auch

Concurrency Support Library