Multithreading C++
Le multithreading est une forme spécialisée de multitâche et un multitâche est la fonctionnalité qui permet à votre ordinateur d'exécuter deux programmes ou plus simultanément. En général, il existe deux types de multitâche :basé sur les processus et basé sur les threads.
Le multitâche basé sur les processus gère l'exécution simultanée de programmes. Le multitâche basé sur les threads traite de l'exécution simultanée de parties du même programme.
Un programme multithread contient deux parties ou plus qui peuvent s'exécuter simultanément. Chaque partie d'un tel programme est appelée un thread, et chaque thread définit un chemin d'exécution séparé.
C++ ne contient aucun support intégré pour les applications multithread. Au lieu de cela, il s'appuie entièrement sur le système d'exploitation pour fournir cette fonctionnalité.
Ce didacticiel suppose que vous travaillez sur le système d'exploitation Linux et que nous allons écrire un programme C++ multithread en utilisant POSIX. Les threads POSIX, ou Pthreads, fournissent des API disponibles sur de nombreux systèmes POSIX de type Unix tels que FreeBSD, NetBSD, GNU/Linux, Mac OS X et Solaris.
Créer des sujets
La routine suivante est utilisée pour créer un thread POSIX −
#include <pthread.h> pthread_create (thread, attr, start_routine, arg)
Ici, pthread_create crée un nouveau thread et le rend exécutable. Cette routine peut être appelée n'importe quel nombre de fois depuis n'importe où dans votre code. Voici la description des paramètres −
Sr.No | Paramètre et description |
---|---|
1 | |
2 | |
3 | |
4 |
Le nombre maximal de threads pouvant être créés par un processus dépend de l'implémentation. Une fois créés, les threads sont pairs et peuvent créer d'autres threads. Il n'y a pas de hiérarchie ou de dépendance implicite entre les threads.
Terminer les threads
Il y a la routine suivante que nous utilisons pour terminer un thread POSIX −
#include <pthread.h> pthread_exit (status)
Ici pthread_exit est utilisé pour quitter explicitement un thread. En règle générale, la routine pthread_exit() est appelée après qu'un thread a terminé son travail et qu'il n'est plus nécessaire d'exister.
Si main() se termine avant les threads qu'il a créés et se termine avec pthread_exit(), les autres threads continueront à s'exécuter. Sinon, ils seront automatiquement terminés lorsque main() se terminera.
Exemple
Cet exemple de code simple crée 5 threads avec la routine pthread_create(). Chaque thread imprime un "Hello World!" message, puis se termine par un appel à pthread_exit().
#include <iostream> #include <cstdlib> #include <pthread.h> using namespace std; #define NUM_THREADS 5 void *PrintHello(void *threadid) { long tid; tid = (long)threadid; cout << "Hello World! Thread ID, " << tid << endl; pthread_exit(NULL); } int main () { pthread_t threads[NUM_THREADS]; int rc; int i; for( i = 0; i < NUM_THREADS; i++ ) { cout << "main() : creating thread, " << i << endl; rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i); if (rc) { cout << "Error:unable to create thread," << rc << endl; exit(-1); } } pthread_exit(NULL); }
Compilez le programme suivant en utilisant la bibliothèque -lpthread comme suit −
$gcc test.cpp -lpthread
Maintenant, exécutez votre programme qui donne la sortie suivante −
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Hello World! Thread ID, 0 Hello World! Thread ID, 1 Hello World! Thread ID, 2 Hello World! Thread ID, 3 Hello World! Thread ID, 4
Passer des arguments aux threads
Cet exemple montre comment passer plusieurs arguments via une structure. Vous pouvez passer n'importe quel type de données dans un rappel de thread car il pointe vers void comme expliqué dans l'exemple suivant −
#include <iostream> #include <cstdlib> #include <pthread.h> using namespace std; #define NUM_THREADS 5 struct thread_data { int thread_id; char *message; }; void *PrintHello(void *threadarg) { struct thread_data *my_data; my_data = (struct thread_data *) threadarg; cout << "Thread ID : " << my_data->thread_id ; cout << " Message : " << my_data->message << endl; pthread_exit(NULL); } int main () { pthread_t threads[NUM_THREADS]; struct thread_data td[NUM_THREADS]; int rc; int i; for( i = 0; i < NUM_THREADS; i++ ) { cout <<"main() : creating thread, " << i << endl; td[i].thread_id = i; td[i].message = "This is message"; rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]); if (rc) { cout << "Error:unable to create thread," << rc << endl; exit(-1); } } pthread_exit(NULL); }
Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant −
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Thread ID : 3 Message : This is message Thread ID : 2 Message : This is message Thread ID : 0 Message : This is message Thread ID : 1 Message : This is message Thread ID : 4 Message : This is message
Joindre et détacher des fils
Il existe deux routines suivantes que nous pouvons utiliser pour joindre ou détacher des threads −
pthread_join (threadid, status) pthread_detach (threadid)
La sous-routine pthread_join() bloque le thread appelant jusqu'à ce que le thread 'threadid' spécifié se termine. Lorsqu'un thread est créé, l'un de ses attributs définit s'il est joignable ou détaché. Seuls les threads créés comme joignables peuvent être joints. Si un fil est créé comme détaché, il ne peut jamais être joint.
Cet exemple montre comment attendre la fin des threads à l'aide de la routine de jointure Pthread.
#include <iostream> #include <cstdlib> #include <pthread.h> #include <unistd.h> using namespace std; #define NUM_THREADS 5 void *wait(void *t) { int i; long tid; tid = (long)t; sleep(1); cout << "Sleeping in thread " << endl; cout << "Thread with id : " << tid << " ...exiting " << endl; pthread_exit(NULL); } int main () { int rc; int i; pthread_t threads[NUM_THREADS]; pthread_attr_t attr; void *status; // Initialize and set thread joinable pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for( i = 0; i < NUM_THREADS; i++ ) { cout << "main() : creating thread, " << i << endl; rc = pthread_create(&threads[i], &attr, wait, (void *)i ); if (rc) { cout << "Error:unable to create thread," << rc << endl; exit(-1); } } // free attribute and wait for the other threads pthread_attr_destroy(&attr); for( i = 0; i < NUM_THREADS; i++ ) { rc = pthread_join(threads[i], &status); if (rc) { cout << "Error:unable to join," << rc << endl; exit(-1); } cout << "Main: completed thread id :" << i ; cout << " exiting with status :" << status << endl; } cout << "Main: program exiting." << endl; pthread_exit(NULL); }
Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant −
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Sleeping in thread Thread with id : 0 .... exiting Sleeping in thread Thread with id : 1 .... exiting Sleeping in thread Thread with id : 2 .... exiting Sleeping in thread Thread with id : 3 .... exiting Sleeping in thread Thread with id : 4 .... exiting Main: completed thread id :0 exiting with status :0 Main: completed thread id :1 exiting with status :0 Main: completed thread id :2 exiting with status :0 Main: completed thread id :3 exiting with status :0 Main: completed thread id :4 exiting with status :0 Main: program exiting.
Langue C