Ich habe fork() in C verwendet, um einen anderen Prozess zu starten. Wie starte ich einen neuen Thread?
Wie starte ich Threads in normalem C?
Da Sie fork() erwähnt haben, gehe ich davon aus, dass Sie sich in diesem Fall auf einem Unix-ähnlichen System befinden POSIX-Threads (normalerweise als pthreads bezeichnet) sind das, was Sie verwenden möchten.
Insbesondere ist pthread_create() die Funktion, die Sie zum Erstellen eines neuen Threads benötigen. Seine Argumente sind:
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void *
(*start_routine)(void *), void * arg);
Das erste Argument ist der zurückgegebene Zeiger auf die Thread-ID. Das zweite Argument sind die Thread-Argumente, die NULL sein können, es sei denn, Sie möchten den Thread mit einer bestimmten Priorität starten. Das dritte Argument ist die vom Thread ausgeführte Funktion. Das vierte Argument ist das einzelne Argument, das an die Thread-Funktion übergeben wird, wenn sie ausgeführt wird.
-
Hier ist ein Beispiel zur Verwendung: timmurphy.org/2010/05/04/…
– gewaltig
18. Juli 2013 um 11:25 Uhr
-
Zählen POSIX-Threads jetzt nicht als “einfaches C”, da der C11-Standard sie definiert?
– Hydronium
6. Oktober 2013 um 18:50 Uhr
-
Während die C11-Multithreading-API ist schwer beeinflusst von POSIX-Threads, sind sie nicht identisch. Daher erscheint es mir nicht richtig zu sagen, dass POSIX-Threads “einfach C” sind.
– Ben Ylvisaker
27. September 2014 um 20:49 Uhr
AFAIK, ANSI C definiert kein Threading, aber es sind verschiedene Bibliotheken verfügbar.
Wenn Sie Windows verwenden, verknüpfen Sie es mit msvcrt und verwenden Sie _beginthread oder _beginthreadex.
Wenn Sie auf anderen Plattformen laufen, sehen Sie sich die pthreads-Bibliothek an (ich bin sicher, dass es auch andere gibt).
Ciro Santilli OurBigBook.com
C11-Gewinde + C11 atomic_int
Zu glibc 2.28 hinzugefügt. Getestet in Ubuntu 18.10 amd64 (kommt mit glic 2.28) und Ubuntu 18.04 (kommt mit glibc 2.27) durch Kompilieren von glibc 2.28 aus der Quelle: Mehrere glibc-Bibliotheken auf einem einzigen Host
Beispiel übernommen aus: https://en.cppreference.com/w/c/language/atomic
Haupt c
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
atomic_int atomic_counter;
int non_atomic_counter;
int mythread(void* thr_data) {
(void)thr_data;
for(int n = 0; n < 1000; ++n) {
++non_atomic_counter;
++atomic_counter;
// for this example, relaxed memory order is sufficient, e.g.
// atomic_fetch_add_explicit(&atomic_counter, 1, memory_order_relaxed);
}
return 0;
}
int main(void) {
thrd_t thr[10];
for(int n = 0; n < 10; ++n)
thrd_create(&thr[n], mythread, NULL);
for(int n = 0; n < 10; ++n)
thrd_join(thr[n], NULL);
printf("atomic %d\n", atomic_counter);
printf("non-atomic %d\n", non_atomic_counter);
}
Kompilieren und ausführen:
gcc -ggdb3 -std=c11 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
Mögliche Ausgabe:
atomic 10000
non-atomic 4341
Der nicht-atomare Zähler ist sehr wahrscheinlich kleiner als der atomare, da der Zugriff über Threads hinweg auf die nicht-atomare Variable schnell erfolgt.
Siehe auch: Wie führt man ein atomares Inkrement durch und ruft in C ab?
Demontageanalyse
Zerlegen mit:
gdb -batch -ex "disassemble/rs mythread" main.out
enthält:
17 ++non_atomic_counter;
0x00000000004007e8 <+8>: 83 05 65 08 20 00 01 addl $0x1,0x200865(%rip) # 0x601054 <non_atomic_counter>
18 __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
0x00000000004007ef <+15>: f0 83 05 61 08 20 00 01 lock addl $0x1,0x200861(%rip) # 0x601058 <atomic_counter>
Wir sehen also, dass das atomare Inkrement auf Befehlsebene mit dem erfolgt f0
Präfix sperren.
Mit aarch64-linux-gnu-gcc
8.2.0 erhalten wir stattdessen:
11 ++non_atomic_counter;
0x0000000000000a28 <+24>: 60 00 40 b9 ldr w0, [x3]
0x0000000000000a2c <+28>: 00 04 00 11 add w0, w0, #0x1
0x0000000000000a30 <+32>: 60 00 00 b9 str w0, [x3]
12 ++atomic_counter;
0x0000000000000a34 <+36>: 40 fc 5f 88 ldaxr w0, [x2]
0x0000000000000a38 <+40>: 00 04 00 11 add w0, w0, #0x1
0x0000000000000a3c <+44>: 40 fc 04 88 stlxr w4, w0, [x2]
0x0000000000000a40 <+48>: a4 ff ff 35 cbnz w4, 0xa34 <mythread+36>
Die atomare Version hat also tatsächlich eine cbnz
Schleife, die bis zum läuft stlxr
Shop gelingen. Beachten Sie, dass ARMv8.1 all dies mit einer einzigen LDADD-Anweisung erledigen kann.
Dies ist analog zu dem, was wir mit C++ erhalten std::atomic
: Was genau ist std::atomic?
Benchmark
MACHEN. Erstellen Sie einen Benchmark, um zu zeigen, dass Atomic langsamer ist.
POSIX-Threads
Haupt c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>
enum CONSTANTS {
NUM_THREADS = 1000,
NUM_ITERS = 1000
};
int global = 0;
int fail = 0;
pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
void* main_thread(void *arg) {
int i;
for (i = 0; i < NUM_ITERS; ++i) {
if (!fail)
pthread_mutex_lock(&main_thread_mutex);
global++;
if (!fail)
pthread_mutex_unlock(&main_thread_mutex);
}
return NULL;
}
int main(int argc, char **argv) {
pthread_t threads[NUM_THREADS];
int i;
fail = argc > 1;
for (i = 0; i < NUM_THREADS; ++i)
pthread_create(&threads[i], NULL, main_thread, NULL);
for (i = 0; i < NUM_THREADS; ++i)
pthread_join(threads[i], NULL);
assert(global == NUM_THREADS * NUM_ITERS);
return EXIT_SUCCESS;
}
Kompilieren und ausführen:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
./main.out 1
Der erste Durchlauf funktioniert einwandfrei, der zweite schlägt wegen fehlender Synchronisation fehl.
Es scheint keine POSIX-standardisierten atomaren Operationen zu geben: UNIX Portable Atomic Operations
Getestet auf Ubuntu 18.04. GitHub-Upstream.
GCC __atomic_*
Einbauten
Für diejenigen, die kein C11 haben, können Sie mit dem atomare Inkremente erreichen __atomic_*
GCC-Erweiterungen.
Haupt c
#define _XOPEN_SOURCE 700
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
enum Constants {
NUM_THREADS = 1000,
};
int atomic_counter;
int non_atomic_counter;
void* mythread(void *arg) {
(void)arg;
for (int n = 0; n < 1000; ++n) {
++non_atomic_counter;
__atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
}
return NULL;
}
int main(void) {
int i;
pthread_t threads[NUM_THREADS];
for (i = 0; i < NUM_THREADS; ++i)
pthread_create(&threads[i], NULL, mythread, NULL);
for (i = 0; i < NUM_THREADS; ++i)
pthread_join(threads[i], NULL);
printf("atomic %d\n", atomic_counter);
printf("non-atomic %d\n", non_atomic_counter);
}
Kompilieren und ausführen:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
Ausgabe und generierte Assembly: wie im Beispiel „C11-Threads“.
Getestet in Ubuntu 16.04 amd64, GCC 6.4.0.
pthreads ist ein guter Anfang, schau mal hier
Threads sind nicht Teil des C-Standards, daher besteht die einzige Möglichkeit, Threads zu verwenden, darin, eine Bibliothek zu verwenden (z API)
Jay Conrod
Probier das aus Gewinde (POSIX-Thread)-Bibliothek.
Es hängt von der Plattform ab. So geht’s unter Windows: http://msdn.microsoft.com/en-us/library/ms682453.aspx
– Eric Z Bart
11. September 2008 um 15:08 Uhr
Wie in der von Ihnen verlinkten CreateThread-Dokumentation erläutert, sollte _beginthreadex und nicht CreateThread verwendet werden, wenn das Programm die C-Laufzeitbibliothek verwendet.
– ChrisN
11. September 2008 um 15:24 Uhr
Danke, das war mir nicht aufgefallen. Woher wissen Sie, ob Sie die CRT verwenden? Ich dachte, alles benutzt es.
– Eric Z Bart
11. September 2008 um 16:19 Uhr
Sie können den Compiler anweisen, die Standardbibliotheken nicht einzuschließen. Dann können Sie die CRT nicht versehentlich verwenden und Sie können die Win32-Funktionen vollständig verwenden.
– Zan Luchs
6. April 2010 um 3:59 Uhr