Ich muss eine atomare Variable in C verwenden, da auf diese Variable über verschiedene Threads zugegriffen wird. Ich will keine Race-Condition.
Mein Code läuft auf CentOS. Welche Möglichkeiten habe ich?
besser
Ich muss eine atomare Variable in C verwenden, da auf diese Variable über verschiedene Threads zugegriffen wird. Ich will keine Race-Condition.
Mein Code läuft auf CentOS. Welche Möglichkeiten habe ich?
Ciro Santilli OurBigBook.com
C11 atomare Primitive
http://en.cppreference.com/w/c/language/atomic
_Atomic const int * p1; // p is a pointer to an atomic const int
const atomic_int * p2; // same
const _Atomic(int) * p3; // same
Hinzugefügt in glibc 2.28. Getestet in Ubuntu 18.04 (glibc 2.27) durch Kompilieren von glibc aus der Quelle: Mehrere glibc-Bibliotheken auf einem einzigen Host Später auch getestet auf Ubuntu 20.04, glibc 2.31.
Beispiel übernommen aus: https://en.cppreference.com/w/c/language/atomic
Haupt c
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
atomic_int acnt;
int cnt;
int f(void* thr_data)
{
(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);
}
Kompilieren und ausführen:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
Mögliche Ausgabe:
The atomic counter is 10000
The non-atomic counter is 8644
Der nicht-atomare Zähler ist sehr wahrscheinlich kleiner als der atomare, da der Zugriff über Threads hinweg auf die nicht-atomare Variable sehr schnell erfolgt.
Disassemblierungsanalyse unter: Wie starte ich Threads in normalem C?
Wenn Sie GCC auf Ihrer CentOS-Plattform verwenden, können Sie die __atomic
eingebaute Funktionen.
Besonders interessant könnte diese Funktion sein:
— Eingebaute Funktion:
bool __atomic_always_lock_free (size_t size, void *ptr)
Diese integrierte Funktion gibt true zurück, wenn Objekte von
size
Bytes generieren immer lock-freie atomare Anweisungen für die Zielarchitektur.size
muss in eine Kompilierzeitkonstante aufgelöst werden und das Ergebnis wird ebenfalls in eine Kompilierzeitkonstante aufgelöst.
ptr
ist ein optionaler Zeiger auf das Objekt, das verwendet werden kann, um die Ausrichtung zu bestimmen. Ein Wert von0
gibt an, dass eine typische Ausrichtung verwendet werden sollte. Der Compiler kann diesen Parameter auch ignorieren.if (_atomic_always_lock_free (sizeof (long long), 0))
Erich Eberhard
Ich werde meine zwei Cent hineinwerfen, falls jemand davon profitiert. Atomare Operationen sind ein großes Problem in Linux. Ich habe einmal gatomic.h verwendet, nur um festzustellen, dass es weg ist. Ich sehe alle möglichen atomaren Optionen mit fragwürdiger Zuverlässigkeit oder Verfügbarkeit – und ich sehe, dass sich die Dinge ständig ändern. Sie können mit Tests komplex sein, die je nach Betriebssystemebene, Prozessor usw. erforderlich sind. Sie können einen Mutex verwenden – nicht nur komplex durch furchtbar langsam.
Obwohl dies in Threads möglicherweise nicht ideal ist, funktioniert dies hervorragend für atomare Operationen auf Shared-Memory-Variablen. Es ist einfach und funktioniert auf jedem Betriebssystem und Prozessor und jeder Konfiguration, die dem Mann (oder der Frau) bekannt ist, absolut zuverlässig, einfach zu programmieren und wird immer funktionieren.
Jeder Code kann mit einem einfachen Primitiv – einem Semaphor – atomar gemacht werden. Es ist etwas, das wahr/falsch, 1/0, ja/nein, gesperrt/entsperrt ist – binär.
Sobald Sie die Semaphore eingerichtet haben:
set semaphore //must be atomic
Machen Sie den ganzen Code, den Sie mögen, der atomar sein wird, da die Semaphore für Sie blockieren wird
release semaphore //must be atomic
Relativ geradlinig, mit Ausnahme der Zeilen “muss atomar sein”.
Es stellt sich heraus, dass Sie Ihren Semaphoren ganz einfach eine Nummer zuweisen (ich verwende eine Definition, damit sie einen Namen wie “#define OPEN_SEM 1” und “#define “CLASS_SEM 2” usw. haben.
Finden Sie Ihre größte Zahl heraus und öffnen Sie bei der Initialisierung Ihres Programms eine Datei in einem Verzeichnis (ich verwende eine nur für diesen Zweck). Wenn nicht vorhanden, erstellen Sie es:
if (ablockfd < 0) { //ablockfd is static in case you want to
//call it over and over
char *get_sy_path();
char lockname[100];
strcpy(lockname, get_sy_path());
strcat(lockname, "/metlock");
ablockfd = open(lockname, O_RDWR);
//error code if ablockfd bad
}
Nun, um ein Semaphor zu erhalten:
Verwenden Sie nun Ihre Semaphornummer, um einen “Datensatz” in Ihrer Datei mit der Länge von einem Byte zu “sperren”. Hinweis: Die Datei belegt niemals tatsächlich Speicherplatz auf der Festplatte, und es findet keine Festplattenoperation statt.
//sem_id is passed in and is set from OPEN_SEM or CLASS_SEM or whatever you call your semaphores.
lseek(ablockfd, sem_id, SEEK_SET); //seeks to the bytes in file of
//your semaphore number
result = lockf(ablockfd, F_LOCK, 1);
if (result != -1) {
//got the semaphore
} else {
//failed
}
Um zu testen, ob die Semaphore gehalten wird:
result = lockf(ablockfd, F_TEST, 1); //after same lseek
Um die Semaphore freizugeben:
result = lockf(ablockfd, F_ULOCK, 1); //after same lseek
Und all die anderen Dinge, die Sie mit lockf tun können – blockieren/nicht blockieren usw.
Hinweis – dies ist VIEL schneller als ein Mutex, es verschwindet, wenn der Prozess stirbt (eine gute Sache), einfach zu codieren, und ich kenne kein Betriebssystem mit einem Prozessor mit einer beliebigen Anzahl von ihnen oder einer Anzahl von Kernen, die dies nicht atomar können einen Datensatz sperren … so einfacher Code, der einfach funktioniert. Die Datei existiert nie wirklich (keine Bytes, aber im Verzeichnis), scheint keine praktische Begrenzung dafür zu sein, wie viele Sie haben können. Ich habe dies jahrelang auf Maschinen ohne einfache atomare Lösungen verwendet.
en.wikipedia.org/wiki/Volatile_variable Auch das kann lesenswert sein: pages.cs.wisc.edu/~remzi/OSTEP/threads-sema.pdf
– Ricky Mutschlechner
15. August 2014 um 1:02 Uhr
Es sieht so aus, als wären pthreads für CentOS verfügbar, sodass Sie Mutex verwenden könnten, um den Zugriff auf Dinge zu synchronisieren.
– Sergej Kalinitschenko
15. August 2014 um 1:08 Uhr
Der Typ
volatile sig_atomic_t
hat garantiert atomaren Zugriff– MM
15. August 2014 um 1:58 Uhr
@RickyMutschlechner:
volatile
MACHT DAS NICHT. Bitte lesen Sie den Wikipedia-Artikel, auf den Sie verlinkt haben: “Operationen auf flüchtigen Variablen sind nicht atomar …”– Dietrich Ep
15. August 2014 um 2:01 Uhr
@RickyMutschlechner: Sie können ein Spin-Lock nicht einfach mit implementieren
volatile
, da der Prozessor IO neu ordnet, auch wenn der Compiler dies nicht tut. Die Frage sagt auch “will keine Race-Bedingung” undvolatile
ist einfach nicht das richtige Werkzeug für den Job, wenn Sie sich Sorgen um die Rennbedingungen machen.– Dietrich Ep
15. August 2014 um 3:41 Uhr