Namensräume
Varianten
Aktionen

Atomare Typen

Von cppreference.com
< c‎ | Sprache

Inhalt

[bearbeiten] Syntax

_Atomic ( Typname ) (1) (seit C11)
_Atomic Typname (2) (seit C11)
1) Verwendung als Typspezifizierer; dies bezeichnet einen neuen atomaren Typ
2) Verwendung als Typqualifizierer; dies bezeichnet die atomare Version von Typname. In dieser Rolle kann er mit const, volatile und restrict gemischt werden, obwohl die atomare Version von Typname im Gegensatz zu anderen Qualifizierern eine andere Größe, Ausrichtung und Objekt-Repräsentation haben kann.
Typname - beliebiger Typ außer Array oder Funktion. Für (1) darf Typname auch kein atomarer oder cvr-qualifizierter Typ sein.

Der Header <stdatomic.h> definiert viele praktische Typ-Aliase, von atomic_bool bis atomic_uintmax_t, die die Verwendung dieses Schlüsselworts mit integrierten und Bibliotheks-Typen vereinfachen.

_Atomic const int* p1;  // p is a pointer to an atomic const int
const atomic_int* p2;   // same
const _Atomic(int)* p3; // same

Wenn die Makrokonstante __STDC_NO_ATOMICS__ vom Compiler definiert ist, wird das Schlüsselwort _Atomic nicht bereitgestellt.

[bearbeiten] Erklärung

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

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

Beachten Sie, dass jedes atomare Objekt zwar seine eigene Modifikationsreihenfolge hat, es aber keine einzige totale Ordnung gibt; verschiedene Threads können Modifikationen an verschiedenen atomaren Objekten in unterschiedlichen Reihenfolgen beobachten.

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

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

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

Integrierte Inkrement- und Dekrementoperatoren sowie zusammengesetzte Zuweisungen sind Read-Modify-Write-Atomoperationen mit totaler sequenziell konsistenter Ordnung (als ob memory_order_seq_cst verwendet würde). Wenn eine weniger strenge Synchronisationssemantik gewünscht ist, können stattdessen die Standardbibliotheksfunktionen verwendet werden.

Atomare Eigenschaften sind nur für lvalue-Ausdrücke sinnvoll. Die Lvalue-zu-Rvalue-Konvertierung (die einen Speicherlesevorgang von einer atomaren Speicherstelle in ein CPU-Register modelliert) entfernt die Atomarität zusammen mit anderen Qualifizierern.

[bearbeiten] Anmerkungen

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

Der Bibliothekstyp sig_atomic_t bietet keine Inter-Thread-Synchronisation oder Speicherordnung, nur Atomarität.

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

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

[bearbeiten] Schlüsselwörter

_Atomic

[bearbeiten] 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

[bearbeiten] Referenzen

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

[bearbeiten] Siehe auch

Bibliothek für nebenläufige Programmierung
C++-Dokumentation für atomic