Multithread C++
Le multithreading est une forme spéciale de multitâche, qui permet à l'ordinateur d'exécuter deux programmes ou plus en même temps. En général, il existe deux types de multitâche : basé sur des processus et basé sur des threads.
Le multitâche basé sur les processus est l'exécution simultanée de programmes.
Le multitâche basé sur les threads est l'exécution simultanée de fragments du même programme.
Un programme multithread se compose de 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 distinct.
C++ ne contient aucune prise en charge intégrée pour les applications multithread. Au lieu de cela, il s'appuie entièrement sur le système d'exploitation pour fournir cette fonctionnalité.
Ce tutoriel suppose que vous utilisez le système d'exploitation Linux et que nous allons écrire un programme C++ multithread en utilisant POSIX. Les threads POSIX ou Pthreads fournissent une API disponible sur une variété de systèmes POSIX de type Unix, tels que FreeBSD, NetBSD, GNU/Linux, Mac OS X et Solaris.
Créer un fil de discussion
Le programme suivant que nous pouvons utiliser pour créer un fil de discussion POSIX :
#include <pthread.h> pthread_create (thread, attr, start_routine, arg)
Ici, pthread_create crée un nouveau fil de discussion et le crée exécutable. Voici une description des paramètres :
参数 | 描述 |
---|---|
thread | 指向线程标识符指针。 |
attr | 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。 |
start_routine | 线程运行函数起始地址,一旦线程被创建就会执行。 |
arg | 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。 |
Lorsque le thread est créé avec succès, la fonction renvoie 0. Si la valeur de retour n'est pas 0, cela signifie que la création du thread a échoué.
Terminer un thread
En utilisant le programme suivant, nous pouvons l'utiliser pour terminer un thread POSIX :
#include <pthread.h> pthread_exit (status)
Ici, pthread_exit est utilisé explicitement Exit un fil. Normalement, la fonction pthread_exit() est appelée lorsque le thread a terminé son travail et n'a plus besoin de continuer à exister.
Si main() se termine avant le thread qu'il a créé et se termine via pthread_exit(), alors les autres threads continueront à s'exécuter. Sinon, ils seront automatiquement terminés à la fin de main().
Exemple
L'exemple de code simple suivant utilise la fonction pthread_create() pour créer 5 threads, chaque thread génère "Bonjour php!" :
#include <iostream> // 必须的头文件是 #include <pthread.h> using namespace std; #define NUM_THREADS 5 // 线程的运行函数 void* say_hello(void* args) { cout << "Hello php!" << endl; } int main() { // 定义线程的 id 变量,多个变量使用数组 pthread_t tids[NUM_THREADS]; for(int i = 0; i < NUM_THREADS; ++i) { //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数 int ret = pthread_create(&tids[i], NULL, say_hello, NULL); if (ret != 0) { cout << "pthread_create error: error_code=" << ret << endl; } } //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来; pthread_exit(NULL); }
Utilisation de la bibliothèque -lpthread Compilez le programme suivant :
$ g++ test.cpp -lpthread -o test.o
Maintenant, l'exécution du programme produira les résultats suivants :
$ ./test.o Hello php! Hello php! Hello php! Hello php! Hello php!
L'exemple de code simple suivant utilise la fonction pthread_create() pour créer 5 threads et recevoir le paramètre entrant . Chaque thread imprime un message "Bonjour php!" et affiche les paramètres reçus, puis appelle pthread_exit() pour terminer le thread.
//文件名:test.cpp #include <iostream> #include <cstdlib> #include <pthread.h> using namespace std; #define NUM_THREADS 5 void *PrintHello(void *threadid) { // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取 int tid = *((int*)threadid); cout << "Hello php! 线程 ID, " << tid << endl; pthread_exit(NULL); } int main () { pthread_t threads[NUM_THREADS]; int indexes[NUM_THREADS];// 用数组来保存i的值 int rc; int i; for( i=0; i < NUM_THREADS; i++ ){ cout << "main() : 创建线程, " << i << endl; indexes[i] = i; //先保存i的值 // 传入的时候必须强制转换为void* 类型,即无类型指针 rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&(indexes[i])); if (rc){ cout << "Error:无法创建线程," << rc << endl; exit(-1); } } pthread_exit(NULL); }
Maintenant, compilez et exécutez le programme, ce qui produira les résultats suivants :
$ g++ test.cpp -lpthread -o test.o $ ./test.o main() : 创建线程, 0 main() : 创建线程, 1 main() : 创建线程, 2 main() : 创建线程, 3 main() : 创建线程, 4 Hello php! 线程 ID, 4 Hello php! 线程 ID, 3 Hello php! 线程 ID, 2 Hello php! 线程 ID, 1 Hello php! 线程 ID, 0
Passer des paramètres au thread
Cet exemple montre comment transmettre plusieurs paramètres à travers une structure . Vous pouvez transmettre des types de données arbitraires dans le rappel du thread car il pointe vers void, comme le montre 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 les résultats suivants :
$ g++ -Wno-write-strings test.cpp -lpthread -o test.o $ ./test.o 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
Rejoindre et détacher des threads
Nous pouvons utiliser les deux fonctions suivantes pour rejoindre ou détacher des threads :
pthread_join (threadid, status) pthread_detach (threadid)
Le sous-programme pthread_join() bloque le programme appelant jusqu'à ce que le thread ID spécifié se termine. Lorsqu'un thread est créé, l'une de ses propriétés définit s'il est joignable ou détaché. Seuls les threads définis comme connectables lors de leur création peuvent être connectés. Si un thread est défini comme détachable lors de sa création, il ne pourra jamais être connecté.
Cet exemple montre comment utiliser la fonction pthread_join() pour attendre la fin du thread.
#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; // 初始化并设置线程为可连接的(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], NULL, wait, (void *)i ); if (rc){ cout << "Error:unable to create thread," << rc << endl; exit(-1); } } // 删除属性,并等待其他线程 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 les résultats suivants :
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 : 4 ...exiting Sleeping in thread Thread with id : 3 ...exiting Sleeping in thread Thread with id : 2 ...exiting Sleeping in thread Thread with id : 1 ...exiting Sleeping in thread Thread with id : 0 ...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.