一直對多線程編程這一塊很陌生,決定花一點時間整理一下。
os:ubuntu 10.04 ?c++
1.最基礎,進程同時創建5個線程,各自調用同一個函數
- #include?<iostream>??
- #include?<pthread.h>?//多線程相關操作頭文件,可移植眾多平臺??
- ??
- using?namespace?std;??
- ??
- #define?NUM_THREADS?5?//線程數??
- ??
- void*?say_hello(?void*?args?)??
- {??
- ????cout?<<?"hello..."?<<?endl;??
- }?//函數返回的是函數指針,便于后面作為參數??
- ??
- int?main()??
- {??
- ????pthread_t?tids[NUM_THREADS];?//線程id??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????????int?ret?=?pthread_create(?&tids[i],?NULL,?say_hello,?NULL?);?//參數:創建的線程id,線程參數,線程運行函數的起始地址,運行函數的參數??
- ????????if(?ret?!=?0?)?//創建線程成功返回0??
- ????????{??
- ????????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????????}??
- ????}??
- ????pthread_exit(?NULL?);?//等待各個線程退出后,進程才結束,否則進程強制結束,線程處于未終止的狀態??
- }??
注意:
1)此為c++程序,故用g++來編譯生成可執行文件,并且要調用處理多線程操作相關的靜態鏈接庫文件pthread。
2)-lpthread 編譯選項到位置可任意,如g++?-lpthread?-o muti_thread_test_1 muti_thread_test_1.cpp
3)注意gcc和g++的區別,轉到此文:點擊打開鏈接
測試結果:
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_1??
- hello...hello...??
- hello...??
- hello...??
- ??
- hello...??
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_1??
- hello...hello...hello...??
- ??
- hello...??
- hello...??
可知,兩次運行的結果會有差別,這不是多線程的特點吧?這顯然沒有同步?還有待進一步探索...
多線程的運行是混亂的,混亂就是正常?
2.線程調用到函數在一個類中,那必須將該函數聲明為靜態函數函數
因為靜態成員函數屬于靜態全局區,線程可以共享這個區域,故可以各自調用。
- #include?<iostream>??
- #include?<pthread.h>??
- ??
- using?namespace?std;??
- ??
- #define?NUM_THREADS?5??
- ??
- class?Hello??
- {??
- public:??
- ????static?void*?say_hello(?void*?args?)??
- ????{??
- ????????cout?<<?"hello..."?<<?endl;??
- ????}??
- };??
- ??
- int?main()??
- {??
- ????pthread_t?tids[NUM_THREADS];??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????????int?ret?=?pthread_create(?&tids[i],?NULL,?Hello::say_hello,?NULL?);??
- ????????if(?ret?!=?0?)??
- ????????{??
- ????????????cout?<<?"pthread_create?error:error_code"?<<?ret?<<?endl;??
- ????????}??
- ????}??
- ????pthread_exit(?NULL?);??
- }??
測試結果:
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_2??
- hello...??
- hello...??
- hello...??
- hello...??
- hello...??
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_2??
- hello...hello...hello...??
- ??
- ??
- hello...??
- hello...??
3.如何在線程調用函數時傳入參數呢?
先看下面修改的代碼,傳入線程編號作為參數:
- #include?<iostream>??
- #include?<pthread.h>?//多線程相關操作頭文件,可移植眾多平臺??
- ??
- using?namespace?std;??
- ??
- #define?NUM_THREADS?5?//線程數??
- ??
- void*?say_hello(?void*?args?)??
- {??
- ????int?i?=?*(?(int*)args?);?//對傳入的參數進行強制類型轉換,由無類型指針轉變為整形指針,再用*讀取其指向到內容??
- ????cout?<<?"hello?in?"?<<?i?<<??endl;??
- }?//函數返回的是函數指針,便于后面作為參數??
- ??
- int?main()??
- {??
- ????pthread_t?tids[NUM_THREADS];?//線程id??
- ????cout?<<?"hello?in?main.."?<<?endl;??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????????int?ret?=?pthread_create(?&tids[i],?NULL,?say_hello,?(void*)&i?);?//傳入到參數必須強轉為void*類型,即無類型指針,&i表示取i的地址,即指向i的指針??
- ????????cout?<<?"Current?pthread?id?=?"?<<?tids[i]?<<?endl;?//用tids數組打印創建的進程id信息??
- ????????if(?ret?!=?0?)?//創建線程成功返回0??
- ????????{??
- ????????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????????}??
- ????}??
- ????pthread_exit(?NULL?);?//等待各個線程退出后,進程才結束,否則進程強制結束,線程處于未終止的狀態??
- }??
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_3??
- hello?in?main..??
- Current?pthread?id?=?3078458224??
- Current?pthread?id?=?3070065520??
- hello?in?hello?in?2??
- 1??
- Current?pthread?id?=?hello?in?2??
- 3061672816??
- Current?pthread?id?=?3053280112??
- hello?in?4??
- Current?pthread?id?=?hello?in?4??
- 3044887408??
這是因為多線程到緣故,主進程還沒開始對i賦值,線程已經開始跑了...?
修改代碼如下:
- #include?<iostream>??
- #include?<pthread.h>?//多線程相關操作頭文件,可移植眾多平臺??
- ??
- using?namespace?std;??
- ??
- #define?NUM_THREADS?5?//線程數??
- ??
- void*?say_hello(?void*?args?)??
- {??
- ????cout?<<?"hello?in?thread?"?<<?*(?(int?*)args?)?<<??endl;??
- }?//函數返回的是函數指針,便于后面作為參數??
- ??
- int?main()??
- {??
- ????pthread_t?tids[NUM_THREADS];?//線程id??
- ????int?indexes[NUM_THREADS];?//用來保存i的值避免被修改??
- ??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????????indexes[i]?=?i;??
- ????????int?ret?=?pthread_create(?&tids[i],?NULL,?say_hello,?(void*)&(indexes[i])?);??
- ????????if(?ret?!=?0?)?//創建線程成功返回0??
- ????????{??
- ????????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????????}??
- ????}??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????????pthread_join(?tids[i],?NULL?);?//pthread_join用來等待一個線程的結束,是一個線程阻塞的函數??
- }??
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_3??
- 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;
- #include?<iostream>??
- #include?<pthread.h>??
- ??
- using?namespace?std;??
- ??
- #define?NUM_THREADS?5??
- ??
- void*?say_hello(?void*?args?)??
- {??
- ????cout?<<?"hello?in?thread?"?<<?*((?int?*?)args)?<<?endl;??
- ????int?status?=?10?+?*((?int?*?)args);?//線程退出時添加退出的信息,status供主程序提取該線程的結束信息??
- ????pthread_exit(?(?void*?)status?);???
- }??
- ??
- int?main()??
- {??
- ????pthread_t?tids[NUM_THREADS];??
- ????int?indexes[NUM_THREADS];??
- ??????
- ????pthread_attr_t?attr;?//線程屬性結構體,創建線程時加入的參數??
- ????pthread_attr_init(?&attr?);?//初始化??
- ????pthread_attr_setdetachstate(?&attr,?PTHREAD_CREATE_JOINABLE?);?//是設置你想要指定線程屬性參數,這個參數表明這個線程是可以join連接的,join功能表示主程序可以等線程結束后再去做某事,實現了主程序和線程同步功能??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????????indexes[i]?=?i;??
- ????????int?ret?=?pthread_create(?&tids[i],?&attr,?say_hello,?(?void*?)&(?indexes[i]?)?);??
- ????????if(?ret?!=?0?)??
- ????????{??
- ????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????}??
- ????}???
- ????pthread_attr_destroy(?&attr?);?//釋放內存???
- ????void?*status;??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????int?ret?=?pthread_join(?tids[i],?&status?);?//主程序join每個線程后取得每個線程的退出信息status??
- ????if(?ret?!=?0?)??
- ????{??
- ????????cout?<<?"pthread_join?error:error_code="?<<?ret?<<?endl;??
- ????}??
- ????else??
- ????{??
- ????????cout?<<?"pthread_join?get?status:"?<<?(long)status?<<?endl;??
- ????}??
- ????}??
- }??
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_4??
- hello?in?thread?hello?in?thread?hello?in?thread?hello?in?thread?0hello?in?thread?321??
- ??
- ??
- ??
- 4??
- pthread_join?get?status:10??
- pthread_join?get?status:11??
- pthread_join?get?status:12??
- pthread_join?get?status:13??
- pthread_join?get?status:14??
5.互斥鎖的實現
互斥鎖是實現線程同步的一種機制,只要在臨界區前后對資源加鎖就能阻塞其他進程的訪問。
- #include?<iostream>??
- #include?<pthread.h>??
- ??
- using?namespace?std;??
- ??
- #define?NUM_THREADS?5??
- ??
- int?sum?=?0;?//定義全局變量,讓所有線程同時寫,這樣就需要鎖機制??
- pthread_mutex_t?sum_mutex;?//互斥鎖??
- ??
- void*?say_hello(?void*?args?)??
- {??
- ????cout?<<?"hello?in?thread?"?<<?*((?int?*?)args)?<<?endl;??
- ????pthread_mutex_lock(?&sum_mutex?);?//先加鎖,再修改sum的值,鎖被占用就阻塞,直到拿到鎖再修改sum;??
- ????cout?<<?"before?sum?is?"?<<?sum?<<?"?in?thread?"?<<?*(?(?int*?)args?)?<<?endl;??
- ????sum?+=?*(?(?int*?)args?);??
- ????cout?<<?"after?sum?is?"?<<?sum?<<?"?in?thread?"?<<?*(?(?int*?)args?)?<<?endl;??
- ????pthread_mutex_unlock(?&sum_mutex?);?//釋放鎖,供其他線程使用??
- ????pthread_exit(?0?);???
- }??
- ??
- int?main()??
- {??
- ????pthread_t?tids[NUM_THREADS];??
- ????int?indexes[NUM_THREADS];??
- ??????
- ????pthread_attr_t?attr;?//線程屬性結構體,創建線程時加入的參數??
- ????pthread_attr_init(?&attr?);?//初始化??
- ????pthread_attr_setdetachstate(?&attr,?PTHREAD_CREATE_JOINABLE?);?//是設置你想要指定線程屬性參數,這個參數表明這個線程是可以join連接的,join功能表示主程序可以等線程結束后再去做某事,實現了主程序和線程同步功能??
- ????pthread_mutex_init(?&sum_mutex,?NULL?);?//對鎖進行初始化??????
- ??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????????indexes[i]?=?i;??
- ????????int?ret?=?pthread_create(?&tids[i],?&attr,?say_hello,?(?void*?)&(?indexes[i]?)?);?//5個進程同時去修改sum??
- ????????if(?ret?!=?0?)??
- ????????{??
- ????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????}??
- ????}???
- ????pthread_attr_destroy(?&attr?);?//釋放內存???
- ????void?*status;??
- ????for(?int?i?=?0;?i?<?NUM_THREADS;?++i?)??
- ????{??
- ????int?ret?=?pthread_join(?tids[i],?&status?);?//主程序join每個線程后取得每個線程的退出信息status??
- ????if(?ret?!=?0?)??
- ????{??
- ????????cout?<<?"pthread_join?error:error_code="?<<?ret?<<?endl;??
- ????}??
- ????}??
- ????cout?<<?"finally?sum?is?"?<<?sum?<<?endl;??
- ????pthread_mutex_destroy(?&sum_mutex?);?//注銷鎖??
- }??
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_5??
- hello?in?thread?hello?in?thread?hello?in?thread?410??
- before?sum?is?hello?in?thread?0?in?thread?4??
- after?sum?is?4?in?thread?4hello?in?thread???
- ??
- ??
- 2??
- 3??
- before?sum?is?4?in?thread?1??
- after?sum?is?5?in?thread?1??
- before?sum?is?5?in?thread?0??
- after?sum?is?5?in?thread?0??
- before?sum?is?5?in?thread?2??
- after?sum?is?7?in?thread?2??
- before?sum?is?7?in?thread?3??
- after?sum?is?10?in?thread?3??
- finally?sum?is?10??
6.信號量的實現
信號量是線程同步的另一種實現機制,信號量的操作有signal和wait,本例子采用條件信號變量pthread_cond_t tasks_cond;
信號量的實現也要給予鎖機制。
- #include?<iostream>??
- #include?<pthread.h>??
- #include?<stdio.h>??
- ??
- using?namespace?std;??
- ??
- #define?BOUNDARY?5??
- ??
- int?tasks?=?10;??
- pthread_mutex_t?tasks_mutex;?//互斥鎖??
- pthread_cond_t?tasks_cond;?//條件信號變量,處理兩個線程間的條件關系,當task>5,hello2處理,反之hello1處理,直到task減為0??
- ??
- void*?say_hello2(?void*?args?)??
- {??
- ????pthread_t?pid?=?pthread_self();?//獲取當前線程id??
- ????cout?<<?"["?<<?pid?<<?"]?hello?in?thread?"?<<??*(?(?int*?)args?)?<<?endl;??
- ??????
- ????bool?is_signaled?=?false;?//sign??
- ????while(1)??
- ????{??
- ????pthread_mutex_lock(?&tasks_mutex?);?//加鎖??
- ????if(?tasks?>?BOUNDARY?)??
- ????{??
- ????????cout?<<?"["?<<?pid?<<?"]?take?task:?"?<<?tasks?<<?"?in?thread?"?<<?*(?(int*)args?)?<<?endl;??
- ????????--tasks;?//modify??
- ????}??
- ????else?if(?!is_signaled?)??
- ????{??
- ????????cout?<<?"["?<<?pid?<<?"]?pthread_cond_signal?in?thread?"?<<?*(?(?int*?)args?)?<<?endl;??
- ????????pthread_cond_signal(?&tasks_cond?);?//signal:向hello1發送信號,表明已經>5??
- ????????is_signaled?=?true;?//表明信號已發送,退出此線程??
- ????}??
- ????pthread_mutex_unlock(?&tasks_mutex?);?//解鎖??
- ????if(?tasks?==?0?)??
- ????????break;??
- ????}??????
- }??
- ??
- void*?say_hello1(?void*?args?)??
- {??
- ????pthread_t?pid?=?pthread_self();?//獲取當前線程id??
- ????cout?<<?"["?<<?pid?<<?"]?hello?in?thread?"?<<??*(?(?int*?)args?)?<<?endl;??
- ??
- ????while(1)??
- ????{??
- ????????pthread_mutex_lock(?&tasks_mutex?);?//加鎖??
- ????????if(?tasks?>?BOUNDARY?)??
- ????????{??
- ????????cout?<<?"["?<<?pid?<<?"]?pthread_cond_signal?in?thread?"?<<?*(?(?int*?)args?)?<<?endl;??
- ????????pthread_cond_wait(?&tasks_cond,?&tasks_mutex?);?//wait:等待信號量生效,接收到信號,向hello2發出信號,跳出wait,執行后續???
- ????????}??
- ????????else??
- ????????{??
- ????????cout?<<?"["?<<?pid?<<?"]?take?task:?"?<<?tasks?<<?"?in?thread?"?<<?*(?(int*)args?)?<<?endl;??
- ????????????--tasks;??
- ????}??
- ????????pthread_mutex_unlock(?&tasks_mutex?);?//解鎖??
- ????????if(?tasks?==?0?)??
- ????????????break;??
- ????}???
- }??
- ??
- ??
- int?main()??
- {??
- ????pthread_attr_t?attr;?//線程屬性結構體,創建線程時加入的參數??
- ????pthread_attr_init(?&attr?);?//初始化??
- ????pthread_attr_setdetachstate(?&attr,?PTHREAD_CREATE_JOINABLE?);?//是設置你想要指定線程屬性參數,這個參數表明這個線程是可以join連接的,join功能表示主程序可以等線程結束后再去做某事,實現了主程序和線程同步功能??
- ????pthread_cond_init(?&tasks_cond,?NULL?);?//初始化條件信號量??
- ????pthread_mutex_init(?&tasks_mutex,?NULL?);?//初始化互斥量??
- ????pthread_t?tid1,?tid2;?//保存兩個線程id??
- ????int?index1?=?1;??
- ????int?ret?=?pthread_create(?&tid1,?&attr,?say_hello1,?(?void*?)&index1?);??
- ????if(?ret?!=?0?)??
- ????{??
- ????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????}??
- ????int?index2?=?2;??
- ????ret?=?pthread_create(?&tid2,?&attr,?say_hello2,?(?void*?)&index2?);??
- ????if(?ret?!=?0?)??
- ????{??
- ????????cout?<<?"pthread_create?error:error_code="?<<?ret?<<?endl;??
- ????}??
- ????pthread_join(?tid1,?NULL?);?//連接兩個線程??
- ????pthread_join(?tid2,?NULL?);???
- ??
- ????pthread_attr_destroy(?&attr?);?//釋放內存???
- ????pthread_mutex_destroy(?&tasks_mutex?);?//注銷鎖??
- ????pthread_cond_destroy(?&tasks_cond?);?//正常退出??
- }??
先在線程2中執行say_hello2,再跳轉到線程1中執行say_hello1,直到tasks減到0為止。
- wq@wq-desktop:~/coding/muti_thread$?./muti_thread_test_6??
- [3069823856]?hello?in?thread?2??
- [3078216560]?hello?in?thread?1[3069823856]?take?task:?10?in?thread?2??
- ??
- [3069823856]?take?task:?9?in?thread?2??
- [3069823856]?take?task:?8?in?thread?2??
- [3069823856]?take?task:?7?in?thread?2??
- [3069823856]?take?task:?6?in?thread?2??
- [3069823856]?pthread_cond_signal?in?thread?2??
- [3078216560]?take?task:?5?in?thread?1??
- [3078216560]?take?task:?4?in?thread?1??
- [3078216560]?take?task:?3?in?thread?1??
- [3078216560]?take?task:?2?in?thread?1??
- [3078216560]?take?task:?1?in?thread?1??