C++ multithreading
Multi-threading ialah satu bentuk khas multitasking, yang membolehkan komputer menjalankan dua atau lebih program pada masa yang sama. Secara umum, terdapat dua jenis multitasking: berasaskan proses dan berasaskan benang.
Berbilang tugas berasaskan proses ialah pelaksanaan program serentak.
Berbilang tugas berasaskan benang ialah pelaksanaan serentak serpihan program yang sama.
Program berbilang benang terdiri daripada dua atau lebih bahagian yang boleh dijalankan serentak. Setiap bahagian program sedemikian dipanggil thread, dan setiap thread mentakrifkan laluan pelaksanaan yang berasingan.
C++ tidak mengandungi sebarang sokongan terbina dalam untuk aplikasi berbilang benang. Sebaliknya, ia bergantung sepenuhnya pada sistem pengendalian untuk menyediakan fungsi ini.
Tutorial ini mengandaikan bahawa anda menggunakan sistem pengendalian Linux dan kami akan menulis program C++ berbilang benang menggunakan POSIX. POSIX Threads atau Pthreads menyediakan API yang tersedia pada pelbagai sistem POSIX seperti Unix, seperti FreeBSD, NetBSD, GNU/Linux, Mac OS X dan Solaris.
Buat Benang
Atur cara berikut yang boleh kita gunakan untuk mencipta utas POSIX:
#include <pthread.h> pthread_create (thread, attr, start_routine, arg)
Di sini, pthread_create mencipta utas baharu dan menjadikannya boleh dilaksanakan. Berikut ialah perihalan parameter:
参数 | 描述 |
---|---|
thread | 指向线程标识符指针。 |
attr | 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。 |
start_routine | 线程运行函数起始地址,一旦线程被创建就会执行。 |
arg | 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。 |
Apabila benang berjaya dibuat, fungsi mengembalikan 0. Jika nilai pulangan bukan 0, ini bermakna penciptaan benang gagal.
Menamatkan utas
Menggunakan atur cara berikut, kita boleh menggunakannya untuk menamatkan utas POSIX:
#include <pthread.h> pthread_exit (status)
Di sini, pthread_exit digunakan secara eksplisit Exit seutas benang. Biasanya, fungsi pthread_exit() dipanggil apabila benang telah menyelesaikan kerjanya dan tidak lagi perlu terus wujud.
Jika main() tamat sebelum urutan yang dicipta dan keluar melalui pthread_exit(), maka urutan lain akan terus dilaksanakan. Jika tidak, ia akan ditamatkan secara automatik pada penghujung main().
Contoh
Kod contoh mudah berikut menggunakan fungsi pthread_create() untuk mencipta 5 thread, setiap thread mengeluarkan "Hello 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); }
Menggunakan perpustakaan -lpthread Susun atur cara berikut:
$ g++ test.cpp -lpthread -o test.o
Sekarang, melaksanakan program akan menghasilkan keputusan berikut:
$ ./test.o Hello php! Hello php! Hello php! Hello php! Hello php!
Kod contoh mudah berikut menggunakan fungsi pthread_create() untuk mencipta 5 utas dan menerima parameter masuk . Setiap utas mencetak mesej "Hello php!" dan mengeluarkan parameter yang diterima, dan kemudian memanggil pthread_exit() untuk menamatkan utas.
//文件名: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); }
Sekarang susun dan laksanakan atur cara, yang akan menghasilkan keputusan berikut:
$ 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
Meluluskan parameter ke utas
Contoh ini menunjukkan cara menghantar berbilang parameter melalui struktur . Anda boleh menghantar jenis data sewenang-wenangnya dalam panggilan balik benang kerana ia menunjuk kepada batal, seperti yang ditunjukkan dalam contoh berikut:
#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); }
Apabila kod di atas disusun dan dilaksanakan, ia menghasilkan keputusan berikut:
$ 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
Sertai dan tanggalkan utas
Kita boleh menggunakan dua fungsi berikut untuk menyertai atau menanggalkan utas:
pthread_join (threadid, status) pthread_detach (threadid)
subrutin pthread_join() menyekat program panggilan sehingga utas berbenang yang ditentukan ditamatkan . Apabila benang dibuat, salah satu sifatnya menentukan sama ada ia boleh dicantum atau ditanggalkan. Hanya benang yang ditakrifkan sebagai boleh disambungkan apabila dibuat boleh disambungkan. Jika benang ditakrifkan sebagai boleh tanggal apabila ia dicipta, ia tidak boleh disambungkan.
Contoh ini menunjukkan cara menggunakan fungsi pthread_join() untuk menunggu urutan selesai.
#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); }
Apabila kod di atas disusun dan dilaksanakan, ia menghasilkan keputusan berikut:
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.