Atomare Typen
Inhalt |
[bearbeiten] Syntax
_Atomic ( Typname ) |
(1) | (seit C11) | |||||||
_Atomic Typname |
(2) | (seit C11) | |||||||
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
- Schreib-Schreib-Kohärenz: Wenn eine Operation
A, die ein atomares ObjektMmodifiziert, happens-before einer OperationB, dieMmodifiziert, dann erscheintAvorBin der Modifikationsreihenfolge vonM. - Lese-Lese-Kohärenz: Wenn eine Wertberechnung
Aeines atomaren ObjektsMvor einer WertberechnungBvonMstattfindet undAseinen Wert aus einem NebeneffektXaufMbezieht, dann ist der vonBberechnete Wert entweder der vonXgespeicherte Wert oder der Wert, der von einem NebeneffektYaufMgespeichert wurde, wobeiYin der Modifikationsreihenfolge vonMnachXauftritt. - Lese-Schreib-Kohärenz: Wenn eine Wertberechnung
Aeines atomaren ObjektsMhappens-before einer OperationBaufM, dann beziehtAseinen Wert aus einem NebeneffektXaufM, wobeiXin der Modifikationsreihenfolge vonMvorBauftritt. - Schreib-Lese-Kohärenz: Wenn ein Nebeneffekt
Xauf ein atomares ObjektMhappens-before eine WertberechnungBvonM, dann bezieht die AuswertungBihren Wert vonXoder von einem NebeneffektY, der in der Modifikationsreihenfolge vonMnachXauftritt.
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.
| Dieser Abschnitt ist unvollständig Grund: Mehr, Überprüfung der Interaktion mit memory_order und atomaren Bibliotheksseiten |
[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
[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
|