c++多線程編程

原文鏈接:https://blog.csdn.net/hitwengqi/article/details/8015646

一直對多線程編程這一塊很陌生,決定花一點時間整理一下。

os:ubuntu 10.04  c++

1.最基礎,進程同時創建5個線程,各自調用同一個函數

  1. #include <iostream>
  2. #include <pthread.h> //多線程相關操作頭文件,可移植衆多平臺
  3. using namespace std;
  4. #define NUM_THREADS 5 //線程數
  5. void* say_hello( void* args )
  6. {
  7. cout << "hello..." << endl;
  8. } //函數返回的是函數指針,便於後面作爲參數
  9. int main()
  10. {
  11. pthread_t tids[NUM_THREADS]; //線程id
  12. for( int i = 0; i < NUM_THREADS; ++i )
  13. {
  14. int ret = pthread_create( &tids[i], NULL, say_hello, NULL ); //參數:創建的線程id,線程參數,線程運行函數的起始地址,運行函數的參數
  15. if( ret != 0 ) //創建線程成功返回0
  16. {
  17. cout << "pthread_create error:error_code=" << ret << endl;
  18. }
  19. }
  20. pthread_exit( NULL ); //等待各個線程退出後,進程才結束,否則進程強制結束,線程處於未終止的狀態
  21. }
輸入命令:g++ -o muti_thread_test_1 muti_thread_test_1.cpp -lpthread

注意:

1)此爲c++程序,故用g++來編譯生成可執行文件,並且要調用處理多線程操作相關的靜態鏈接庫文件pthread。

2)-lpthread 編譯選項到位置可任意,如g++ -lpthread -o muti_thread_test_1 muti_thread_test_1.cpp

3)注意gcc和g++的區別,轉到此文:點擊打開鏈接

測試結果:

  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_1
  2. hello...hello...
  3. hello...
  4. hello...
  5. hello...
  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_1
  2. hello...hello...hello...
  3. hello...
  4. hello...

可知,兩次運行的結果會有差別,這不是多線程的特點吧?這顯然沒有同步?還有待進一步探索...

多線程的運行是混亂的,混亂就是正常?


2.線程調用到函數在一個類中,那必須將該函數聲明爲靜態函數函數

因爲靜態成員函數屬於靜態全局區,線程可以共享這個區域,故可以各自調用。

  1. #include <iostream>
  2. #include <pthread.h>
  3. using namespace std;
  4. #define NUM_THREADS 5
  5. class Hello
  6. {
  7. public:
  8. static void* say_hello( void* args )
  9. {
  10. cout << "hello..." << endl;
  11. }
  12. };
  13. int main()
  14. {
  15. pthread_t tids[NUM_THREADS];
  16. for( int i = 0; i < NUM_THREADS; ++i )
  17. {
  18. int ret = pthread_create( &tids[i], NULL, Hello::say_hello, NULL );
  19. if( ret != 0 )
  20. {
  21. cout << "pthread_create error:error_code" << ret << endl;
  22. }
  23. }
  24. pthread_exit( NULL );
  25. }

測試結果:

  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_2
  2. hello...
  3. hello...
  4. hello...
  5. hello...
  6. hello...
  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_2
  2. hello...hello...hello...
  3. hello...
  4. hello...

3.如何在線程調用函數時傳入參數呢?

先看下面修改的代碼,傳入線程編號作爲參數:

  1. #include <iostream>
  2. #include <pthread.h> //多線程相關操作頭文件,可移植衆多平臺
  3. using namespace std;
  4. #define NUM_THREADS 5 //線程數
  5. void* say_hello( void* args )
  6. {
  7. int i = *( (int*)args ); //對傳入的參數進行強制類型轉換,由無類型指針轉變爲整形指針,再用*讀取其指向到內容
  8. cout << "hello in " << i << endl;
  9. } //函數返回的是函數指針,便於後面作爲參數
  10. int main()
  11. {
  12. pthread_t tids[NUM_THREADS]; //線程id
  13. cout << "hello in main.." << endl;
  14. for( int i = 0; i < NUM_THREADS; ++i )
  15. {
  16. int ret = pthread_create( &tids[i], NULL, say_hello, (void*)&i ); //傳入到參數必須強轉爲void*類型,即無類型指針,&i表示取i的地址,即指向i的指針
  17. cout << "Current pthread id = " << tids[i] << endl; //用tids數組打印創建的進程id信息
  18. if( ret != 0 ) //創建線程成功返回0
  19. {
  20. cout << "pthread_create error:error_code=" << ret << endl;
  21. }
  22. }
  23. pthread_exit( NULL ); //等待各個線程退出後,進程才結束,否則進程強制結束,線程處於未終止的狀態
  24. }
測試結果:
  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_3
  2. hello in main..
  3. Current pthread id = 3078458224
  4. Current pthread id = 3070065520
  5. hello in hello in 2
  6. 1
  7. Current pthread id = hello in 2
  8. 3061672816
  9. Current pthread id = 3053280112
  10. hello in 4
  11. Current pthread id = hello in 4
  12. 3044887408
顯然不是想要的結果,調用順序很亂,這是爲什麼呢?

這是因爲多線程到緣故,主進程還沒開始對i賦值,線程已經開始跑了...?

修改代碼如下:

  1. #include <iostream>
  2. #include <pthread.h> //多線程相關操作頭文件,可移植衆多平臺
  3. using namespace std;
  4. #define NUM_THREADS 5 //線程數
  5. void* say_hello( void* args )
  6. {
  7. cout << "hello in thread " << *( (int *)args ) << endl;
  8. } //函數返回的是函數指針,便於後面作爲參數
  9. int main()
  10. {
  11. pthread_t tids[NUM_THREADS]; //線程id
  12. int indexes[NUM_THREADS]; //用來保存i的值避免被修改
  13. for( int i = 0; i < NUM_THREADS; ++i )
  14. {
  15. indexes[i] = i;
  16. int ret = pthread_create( &tids[i], NULL, say_hello, (void*)&(indexes[i]) );
  17. if( ret != 0 ) //創建線程成功返回0
  18. {
  19. cout << "pthread_create error:error_code=" << ret << endl;
  20. }
  21. }
  22. for( int i = 0; i < NUM_THREADS; ++i )
  23. pthread_join( tids[i], NULL ); //pthread_join用來等待一個線程的結束,是一個線程阻塞的函數
  24. }
測試結果:

  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_3
  2. hello in thread hello in thread hello in thread hello in thread hello in thread 30124

這是正常的嗎?感覺還是有問題...待續

代碼中如果沒有pthread_join主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。加入pthread_join後,主線程會一直等待直到等待的線程結束自己才結束,使創建的線程有機會執行


4.線程創建時屬性參數的設置pthread_attr_t及join功能的使用

線程的屬性由結構體pthread_attr_t進行管理。

typedef struct
{
    int                           detachstate;     線程的分離狀態
    int                          schedpolicy;   線程調度策略
    struct sched_param      schedparam;   線程的調度參數
    int inheritsched; 線程的繼承性 
    int scope; 線程的作用域 
    size_t guardsize; 線程棧末尾的警戒緩衝區大小 
    int stackaddr_set; void * stackaddr; 線程棧的位置 
    size_t stacksize; 線程棧的大小
}pthread_attr_t;

  1. #include <iostream>
  2. #include <pthread.h>
  3. using namespace std;
  4. #define NUM_THREADS 5
  5. void* say_hello( void* args )
  6. {
  7. cout << "hello in thread " << *(( int * )args) << endl;
  8. int status = 10 + *(( int * )args); //線程退出時添加退出的信息,status供主程序提取該線程的結束信息
  9. pthread_exit( ( void* )status );
  10. }
  11. int main()
  12. {
  13. pthread_t tids[NUM_THREADS];
  14. int indexes[NUM_THREADS];
  15. pthread_attr_t attr; //線程屬性結構體,創建線程時加入的參數
  16. pthread_attr_init( &attr ); //初始化
  17. pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是設置你想要指定線程屬性參數,這個參數表明這個線程是可以join連接的,join功能表示主程序可以等線程結束後再去做某事,實現了主程序和線程同步功能
  18. for( int i = 0; i < NUM_THREADS; ++i )
  19. {
  20. indexes[i] = i;
  21. int ret = pthread_create( &tids[i], &attr, say_hello, ( void* )&( indexes[i] ) );
  22. if( ret != 0 )
  23. {
  24. cout << "pthread_create error:error_code=" << ret << endl;
  25. }
  26. }
  27. pthread_attr_destroy( &attr ); //釋放內存
  28. void *status;
  29. for( int i = 0; i < NUM_THREADS; ++i )
  30. {
  31. int ret = pthread_join( tids[i], &status ); //主程序join每個線程後取得每個線程的退出信息status
  32. if( ret != 0 )
  33. {
  34. cout << "pthread_join error:error_code=" << ret << endl;
  35. }
  36. else
  37. {
  38. cout << "pthread_join get status:" << (long)status << endl;
  39. }
  40. }
  41. }
測試結果:

  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_4
  2. hello in thread hello in thread hello in thread hello in thread 0hello in thread 321
  3. 4
  4. pthread_join get status:10
  5. pthread_join get status:11
  6. pthread_join get status:12
  7. pthread_join get status:13
  8. pthread_join get status:14

5.互斥鎖的實現
互斥鎖是實現線程同步的一種機制,只要在臨界區前後對資源加鎖就能阻塞其他進程的訪問。

  1. #include <iostream>
  2. #include <pthread.h>
  3. using namespace std;
  4. #define NUM_THREADS 5
  5. int sum = 0; //定義全局變量,讓所有線程同時寫,這樣就需要鎖機制
  6. pthread_mutex_t sum_mutex; //互斥鎖
  7. void* say_hello( void* args )
  8. {
  9. cout << "hello in thread " << *(( int * )args) << endl;
  10. pthread_mutex_lock( &sum_mutex ); //先加鎖,再修改sum的值,鎖被佔用就阻塞,直到拿到鎖再修改sum;
  11. cout << "before sum is " << sum << " in thread " << *( ( int* )args ) << endl;
  12. sum += *( ( int* )args );
  13. cout << "after sum is " << sum << " in thread " << *( ( int* )args ) << endl;
  14. pthread_mutex_unlock( &sum_mutex ); //釋放鎖,供其他線程使用
  15. pthread_exit( 0 );
  16. }
  17. int main()
  18. {
  19. pthread_t tids[NUM_THREADS];
  20. int indexes[NUM_THREADS];
  21. pthread_attr_t attr; //線程屬性結構體,創建線程時加入的參數
  22. pthread_attr_init( &attr ); //初始化
  23. pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是設置你想要指定線程屬性參數,這個參數表明這個線程是可以join連接的,join功能表示主程序可以等線程結束後再去做某事,實現了主程序和線程同步功能
  24. pthread_mutex_init( &sum_mutex, NULL ); //對鎖進行初始化
  25. for( int i = 0; i < NUM_THREADS; ++i )
  26. {
  27. indexes[i] = i;
  28. int ret = pthread_create( &tids[i], &attr, say_hello, ( void* )&( indexes[i] ) ); //5個進程同時去修改sum
  29. if( ret != 0 )
  30. {
  31. cout << "pthread_create error:error_code=" << ret << endl;
  32. }
  33. }
  34. pthread_attr_destroy( &attr ); //釋放內存
  35. void *status;
  36. for( int i = 0; i < NUM_THREADS; ++i )
  37. {
  38. int ret = pthread_join( tids[i], &status ); //主程序join每個線程後取得每個線程的退出信息status
  39. if( ret != 0 )
  40. {
  41. cout << "pthread_join error:error_code=" << ret << endl;
  42. }
  43. }
  44. cout << "finally sum is " << sum << endl;
  45. pthread_mutex_destroy( &sum_mutex ); //註銷鎖
  46. }
測試結果:
  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_5
  2. hello in thread hello in thread hello in thread 410
  3. before sum is hello in thread 0 in thread 4
  4. after sum is 4 in thread 4hello in thread
  5. 2
  6. 3
  7. before sum is 4 in thread 1
  8. after sum is 5 in thread 1
  9. before sum is 5 in thread 0
  10. after sum is 5 in thread 0
  11. before sum is 5 in thread 2
  12. after sum is 7 in thread 2
  13. before sum is 7 in thread 3
  14. after sum is 10 in thread 3
  15. finally sum is 10
可知,sum的訪問和修改順序是正常的,這就達到了多線程的目的了,但是線程的運行順序是混亂的,混亂就是正常?

6.信號量的實現
信號量是線程同步的另一種實現機制,信號量的操作有signal和wait,本例子採用條件信號變量pthread_cond_t tasks_cond;
信號量的實現也要給予鎖機制。

  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <stdio.h>
  4. using namespace std;
  5. #define BOUNDARY 5
  6. int tasks = 10;
  7. pthread_mutex_t tasks_mutex; //互斥鎖
  8. pthread_cond_t tasks_cond; //條件信號變量,處理兩個線程間的條件關係,當task>5,hello2處理,反之hello1處理,直到task減爲0
  9. void* say_hello2( void* args )
  10. {
  11. pthread_t pid = pthread_self(); //獲取當前線程id
  12. cout << "[" << pid << "] hello in thread " << *( ( int* )args ) << endl;
  13. bool is_signaled = false; //sign
  14. while(1)
  15. {
  16. pthread_mutex_lock( &tasks_mutex ); //加鎖
  17. if( tasks > BOUNDARY )
  18. {
  19. cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;
  20. --tasks; //modify
  21. }
  22. else if( !is_signaled )
  23. {
  24. cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;
  25. pthread_cond_signal( &tasks_cond ); //signal:向hello1發送信號,表明已經>5
  26. is_signaled = true; //表明信號已發送,退出此線程
  27. }
  28. pthread_mutex_unlock( &tasks_mutex ); //解鎖
  29. if( tasks == 0 )
  30. break;
  31. }
  32. }
  33. void* say_hello1( void* args )
  34. {
  35. pthread_t pid = pthread_self(); //獲取當前線程id
  36. cout << "[" << pid << "] hello in thread " << *( ( int* )args ) << endl;
  37. while(1)
  38. {
  39. pthread_mutex_lock( &tasks_mutex ); //加鎖
  40. if( tasks > BOUNDARY )
  41. {
  42. cout << "[" << pid << "] pthread_cond_signal in thread " << *( ( int* )args ) << endl;
  43. pthread_cond_wait( &tasks_cond, &tasks_mutex ); //wait:等待信號量生效,接收到信號,向hello2發出信號,跳出wait,執行後續
  44. }
  45. else
  46. {
  47. cout << "[" << pid << "] take task: " << tasks << " in thread " << *( (int*)args ) << endl;
  48. --tasks;
  49. }
  50. pthread_mutex_unlock( &tasks_mutex ); //解鎖
  51. if( tasks == 0 )
  52. break;
  53. }
  54. }
  55. int main()
  56. {
  57. pthread_attr_t attr; //線程屬性結構體,創建線程時加入的參數
  58. pthread_attr_init( &attr ); //初始化
  59. pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是設置你想要指定線程屬性參數,這個參數表明這個線程是可以join連接的,join功能表示主程序可以等線程結束後再去做某事,實現了主程序和線程同步功能
  60. pthread_cond_init( &tasks_cond, NULL ); //初始化條件信號量
  61. pthread_mutex_init( &tasks_mutex, NULL ); //初始化互斥量
  62. pthread_t tid1, tid2; //保存兩個線程id
  63. int index1 = 1;
  64. int ret = pthread_create( &tid1, &attr, say_hello1, ( void* )&index1 );
  65. if( ret != 0 )
  66. {
  67. cout << "pthread_create error:error_code=" << ret << endl;
  68. }
  69. int index2 = 2;
  70. ret = pthread_create( &tid2, &attr, say_hello2, ( void* )&index2 );
  71. if( ret != 0 )
  72. {
  73. cout << "pthread_create error:error_code=" << ret << endl;
  74. }
  75. pthread_join( tid1, NULL ); //連接兩個線程
  76. pthread_join( tid2, NULL );
  77. pthread_attr_destroy( &attr ); //釋放內存
  78. pthread_mutex_destroy( &tasks_mutex ); //註銷鎖
  79. pthread_cond_destroy( &tasks_cond ); //正常退出
  80. }
測試結果:
先在線程2中執行say_hello2,再跳轉到線程1中執行say_hello1,直到tasks減到0爲止。

  1. wq@wq-desktop:~/coding/muti_thread$ ./muti_thread_test_6
  2. [3069823856] hello in thread 2
  3. [3078216560] hello in thread 1[3069823856] take task: 10 in thread 2
  4. [3069823856] take task: 9 in thread 2
  5. [3069823856] take task: 8 in thread 2
  6. [3069823856] take task: 7 in thread 2
  7. [3069823856] take task: 6 in thread 2
  8. [3069823856] pthread_cond_signal in thread 2
  9. [3078216560] take task: 5 in thread 1
  10. [3078216560] take task: 4 in thread 1
  11. [3078216560] take task: 3 in thread 1
  12. [3078216560] take task: 2 in thread 1
  13. [3078216560] take task: 1 in thread 1
到此,對多線程編程有了一個初步的瞭解,當然還有其他實現線程同步的機制,有待進一步探索。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章