什么是多線程編程?
1、線程和進(jìn)程的區(qū)別
進(jìn)程是指正在運(yùn)行的程序,它擁有獨(dú)立的內(nèi)存空間和系統(tǒng)資源,不同進(jìn)程之間的數(shù)據(jù)不共享。
線程是進(jìn)程內(nèi)的執(zhí)行單元,它與同一進(jìn)程內(nèi)的其他線程共享進(jìn)程的內(nèi)存空間和系統(tǒng)資源。
2、多線程的優(yōu)勢和應(yīng)用場景
多線程是一種并發(fā)編程方式,它的優(yōu)勢包括:
提高程序的響應(yīng)速度和運(yùn)行效率(多核CPU下的多線程)
充分利用CPU資源,提高系統(tǒng)的利用率
支持多個任務(wù)并行執(zhí)行,提高程序的可擴(kuò)展性和可維護(hù)性
Linux下的多線程編程
Linux下C語言多線程編程依賴于pthread多線程庫。pthread庫是Linux的多線程庫,是POSIX標(biāo)準(zhǔn)線程API的實(shí)現(xiàn),它提供了一種創(chuàng)建和操縱線程的方法,以及一些同步機(jī)制,如互斥鎖、條件變量等。
頭文件:
#include
編譯鏈接需要鏈接鏈接庫 pthread。
一、線程的基本操作
1、pthread_create
/** *@brief創(chuàng)建一個線程 * *Detailedfunctiondescription * *@param[in] thread:一個指向線程標(biāo)識符的指針,線程調(diào)用后,該值被設(shè)置為線程ID;pthread_t為unsigned long int *@param[in]attr:用來設(shè)置線程屬性 *@param[in]start_routine:線程函數(shù)體,線程創(chuàng)建成功后,thread指向的內(nèi)存單元從該地址開始運(yùn)行 *@param[in]arg:傳遞給線程函數(shù)體的參數(shù) * *@return線程創(chuàng)建成功,則返回0,失敗則返回錯誤碼,并且thread內(nèi)容是未定義的 */ intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);
例子test.c:創(chuàng)建一個線程,每1s打印一次。
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; void*thread_fun(void*arg) { s_thread_running=1; while(s_thread_running) { printf("threadrun... "); sleep(1); } pthread_exit(NULL); } intmain(void) { intret=0; printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("thread_createerror! "); exit(EXIT_FAILURE); } ret=pthread_join(s_thread_id,NULL);///
編譯、運(yùn)行:
gcctest.c-otest-lpthread
2、pthread_join
/** *@brief等待某個線程結(jié)束 * *Detailedfunctiondescription:這是一個線程阻塞函數(shù),調(diào)用該函數(shù)則等到線程結(jié)束才繼續(xù)運(yùn)行 * *@param[in]thread:某個線程的ID *@param[in]retval:用于獲取線程start_routine的返回值 * *@return線程創(chuàng)建成功,則返回0,失敗則返回錯誤碼,并且thread內(nèi)容是未定義的 */ intpthread_join(pthread_tthread,void**retval);
例子test.c:創(chuàng)建一個線程,進(jìn)行一次加法運(yùn)算就返回。
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; void*thread_fun(void*arg) { staticintres=0; inta=1,b=2; res=a+b; sleep(1); printf("threadrun,a+b=%d,addr=%p ",res,&res); pthread_exit(&res); } intmain(void) { intret=0; int*retval=NULL; printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("pthread_createerror! "); exit(EXIT_FAILURE); } ret=pthread_join(s_thread_id,(void**)&retval);///
編譯、運(yùn)行:
3、pthread_exit
/** *@brief退出線程 * *Detailedfunctiondescription * *@param[in]retval:它指向的數(shù)據(jù)將作為線程退出時的返回值 * *@returnvoid */ voidpthread_exit(void*retval);
線程將指定函數(shù)體中的代碼執(zhí)行完后自行結(jié)束;
線程執(zhí)行過程中,被同一進(jìn)程中的其它線程(包括主線程)強(qiáng)制終止;
線程執(zhí)行過程中,遇到 pthread_exit() 函數(shù)結(jié)束執(zhí)行。
例子test.c:創(chuàng)建一個線程,每個1s打印一次,打印超過5次時調(diào)用pthread_exit退出。
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; conststaticchar*thread_exit_str="thread_exitok!"; void*thread_fun(void*arg) { staticintcnt=0; s_thread_running=1; while(s_thread_running) { cnt++; if(cnt>5) { pthread_exit((void*)thread_exit_str); } printf("threadrun... "); sleep(1); } pthread_exit(NULL); } intmain(void) { intret=0; void*thread_res=NULL; printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("thread_createerror! "); exit(EXIT_FAILURE); } ret=pthread_join(s_thread_id,(void**)&thread_res); if(ret!=0) { printf("thread_joinerror! "); exit(EXIT_FAILURE); } printf("AfterThread,thread_res=%s ",(char*)thread_res); exit(EXIT_SUCCESS); }
編譯、運(yùn)行:
使用return退出線程與使用pthread_exit退出線程的區(qū)別?
return為通用的函數(shù)退出操作,pthread_exit專用與線程,既然pthread庫有提供專門的函數(shù),自然用pthread_exit會好些,雖然使用return也可以。
看看return退出線程與使用pthread_exit退出線程的具體區(qū)別:退出主線程。使用pthread_exit退出主線程只會終止當(dāng)前線程,不會影響進(jìn)程中其它線程的執(zhí)行;使用return退出主線程,主線程退出執(zhí)行很快,所有線程都會退出。
例子:使用pthread_exit退出主線程
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; conststaticchar*thread_exit_str="thread_exitok!"; void*thread_fun(void*arg) { sleep(1); printf("thread_funrun... "); pthread_exit(NULL); } intmain(void) { intret=0; void*thread_res=NULL; printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("thread_createerror! "); exit(EXIT_FAILURE); } printf("mainthreadexit "); pthread_exit(NULL); }
編譯、運(yùn)行:
例子:使用return退出主線程
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; conststaticchar*thread_exit_str="thread_exitok!"; void*thread_fun(void*arg) { sleep(1); printf("thread_funrun... "); pthread_exit(NULL); } intmain(void) { intret=0; void*thread_res=NULL; printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("thread_createerror! "); exit(EXIT_FAILURE); } printf("mainthreadexit "); return0; }
編譯、運(yùn)行:
4、pthread_self
/** *@brief用來獲取當(dāng)前線程ID * *Detailedfunctiondescription * *@param[in]void * *@return返回線程id */ pthread_tpthread_self(void);
例子:
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; conststaticchar*thread_exit_str="thread_exitok!"; void*thread_fun(void*arg) { staticintcnt=0; s_thread_running=1; while(s_thread_running) { cnt++; if(cnt>5) { pthread_exit((void*)thread_exit_str); } printf("threadrun(tid=%ld)... ",pthread_self()); sleep(1); } pthread_exit(NULL); } intmain(void) { intret=0; void*thread_res=NULL; pid_tpid=getpid(); printf("pid=%d ",pid); printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("thread_createerror! "); exit(EXIT_FAILURE); } ret=pthread_join(s_thread_id,(void**)&thread_res); if(ret!=0) { printf("thread_joinerror! "); exit(EXIT_FAILURE); } printf("AfterThread,thread_res=%s ",(char*)thread_res); exit(EXIT_SUCCESS); }
編譯、運(yùn)行:
5、pthraad_detach
/** *@brief分離線程 * *Detailedfunctiondescription:分離線程,線程結(jié)束是系統(tǒng)自動回收線程的資源 * *@param[in]thread:某個線程的ID * *@return成功時返回0,失敗返回其他值 */ intpthread_detach(pthread_tthread);
pthread_create創(chuàng)建的線程有兩種狀態(tài):joinable(可結(jié)合的)和unjoinable(不可結(jié)合的/分離的)。默認(rèn)是joinable 狀態(tài)。
一個可結(jié)合的線程能夠被其他線程收回其資源和殺死;在被其他線程回收之前,它的存儲器資源(如棧)是不釋放的,所以以默認(rèn)的屬性創(chuàng)建線程時,創(chuàng)建的線程時可結(jié)合的,我們需要對線程退出時調(diào)用pthread_join對線程資源進(jìn)行回收。只有當(dāng)pthread_join函數(shù)返回時,創(chuàng)建的線程才算終止,才能釋放自己占用的系統(tǒng)資源。
一個不可結(jié)合的線程,線程結(jié)束后會自動釋放占用資源。
因?yàn)閜thread_join是一個阻塞的操作,而大多數(shù)時候主線程并不希望因?yàn)檎{(diào)用pthread_join而阻塞,并且大多數(shù)情況下不會使用線程函數(shù)體的返回值,所以這時候可以把線程創(chuàng)建為不可結(jié)合的/分離的。
把線程創(chuàng)建為不可結(jié)合的/分離的有兩種方式:
在創(chuàng)建線程之后,使用pthraad_detach分離線程。
在創(chuàng)建線程之前,使用pthread_attr_setdetachstate設(shè)置線程以不可結(jié)合的/分離的狀態(tài)創(chuàng)建。
例子:在創(chuàng)建線程之后,使用pthraad_detach分離線程。
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; void*thread_fun(void*arg) { s_thread_running=1; while(s_thread_running) { printf("childthreadrun... "); sleep(1); } pthread_exit(NULL); } intmain(void) { intret=0; printf("BeforeThread "); ret=pthread_create(&s_thread_id,NULL,thread_fun,NULL); if(ret!=0) { printf("thread_createerror! "); exit(EXIT_FAILURE); } ret=pthread_detach(s_thread_id); if(ret!=0) { printf("pthread_detacherror! "); exit(EXIT_FAILURE); } printf("AfterThread "); while(1) { printf("mainthreadrun... "); sleep(1); } exit(EXIT_SUCCESS); }
編譯、運(yùn)行:
pthread_join與pthraad_detach的區(qū)別:
pthread_detach()即主線程與子線程分離,兩者相互不干涉,子線程結(jié)束同時子線程的資源自動回收。
pthread_join()即是子線程合入主線程,主線程會一直阻塞,直到子線程執(zhí)行結(jié)束,然后回收子線程資源,并繼續(xù)執(zhí)行。
6、pthread_attr_init
/** *@brief初始化一個線程對象的屬性 * *Detailedfunctiondescription * *@param[in]attr:指向一個線程屬性的指針 * *@return成功時返回0,失敗返回其他值 */ intpthread_attr_init(pthread_attr_t*attr);
如果不設(shè)置線程屬性,線程則以默認(rèn)屬性進(jìn)行創(chuàng)建,默認(rèn)的屬性值如:
例子:在創(chuàng)建線程之前,使用pthread_attr_setdetachstate設(shè)置線程以不可結(jié)合的/分離的狀態(tài)創(chuàng)建。
#include #include #include #include staticpthread_ts_thread_id; staticunsignedchars_thread_running=0; void*thread_fun(void*arg) { s_thread_running=1; while(s_thread_running) { printf("threadrun... "); sleep(1); } pthread_exit(NULL); } intmain(void) { intret=0; printf("BeforeThread "); pthread_attr_tattr; ret=pthread_attr_init(&attr); if(ret!=0) { printf("pthread_attr_initerror! "); exit(EXIT_FAILURE); } pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);///
二、互斥鎖(mutex)的使用
互斥鎖用于保護(hù)一些公共資源。一些公共資源有可能會被多個線程共同使用,如果不做資源保護(hù),可能會產(chǎn)生意想不到的bug。
一個線程,如果需要訪問公共資源,需要獲得互斥鎖并對其加鎖,資源在在鎖定過程中,如果其它線程對其進(jìn)行訪問,也需要獲得互斥鎖,如果獲取不到,線程只能進(jìn)行阻塞,直到獲得該鎖的線程解鎖。
互斥鎖API:
#include ///
互斥鎖有兩種創(chuàng)建方式:
靜態(tài)創(chuàng)建:
pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;
動態(tài)創(chuàng)建:
pthread_mutex_tmutex; pthread_mutex_init(&mutex,NULL);
pthread互斥鎖屬性包括:
PTHREAD_MUTEX_TIMED_NP:這是缺省值,也就是普通鎖。當(dāng)一個線程加鎖以后,其余請求鎖的線程將會形成一個等待隊(duì)列,并在解鎖后按優(yōu)先級獲得鎖。這種策略可以確保資源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖。允許同一個線程對同一個鎖成功獲得多次,并通過unlock解鎖。如果是不同線程請求,則在加鎖線程解鎖時重新競爭。
PTHREAD_MUTEX_ERRORCHECK_NP:檢錯鎖。如果同一個線程請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同,這樣就保證了當(dāng)不允許多次加鎖時不會出現(xiàn)最簡單情況下的死鎖。
PTHREAD_MUTEX_ADAPTIVE_NP:適應(yīng)鎖,動作最簡單的鎖類型,僅等待一小段時間,如果不能獲得鎖就放棄等待
互斥鎖使用形式:
pthread_mutex_tmutex; pthread_mutex_init(&mutex,NULL);///
例子:
#include #include #include #include staticpthread_ts_thread1_id; staticpthread_ts_thread2_id; staticunsignedchars_thread1_running=0; staticunsignedchars_thread2_running=0; staticpthread_mutex_ts_mutex; staticints_cnt=0; void*thread1_fun(void*arg) { printf("[%s]pthread_mutex_lock------s_cnt=%d ",__FUNCTION__,s_cnt); pthread_mutex_lock(&s_mutex);///
編譯、運(yùn)行:
三、條件變量的使用
條件變量是在線程中以睡眠的方式等待某一條件的發(fā)生,是利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制。
條件變量是線程可用的一種同步機(jī)制,條件變量給多個線程提供了一個會合的場所,條件變量與互斥量一起使用時,允許線程以無競爭的方式等待特定的條件發(fā)生。
條件變量API:
#include ///
假如有兩個線程,線程1依賴于某個變量才能執(zhí)行相應(yīng)的操作,而這個變量正好是由線程2來改變的。這種情況下有兩種方案編寫程序:
方案一:線程1輪詢的方式檢測這個變量是否變化,變化則執(zhí)行相應(yīng)的操作。
方案二:使用條件變量的方式。線程1等待線程2滿足條件時進(jìn)行喚醒。
其中,方案一比較浪費(fèi)CPU資源。
條件變量的例子:創(chuàng)建兩個線程,線程1對全局計(jì)數(shù)變量cnt從0開始進(jìn)行自增操作。線程2打印5的倍數(shù),線程1打印其它數(shù)。
#include #include #include #include staticpthread_ts_thread1_id; staticpthread_ts_thread2_id; staticunsignedchars_thread1_running=0; staticunsignedchars_thread2_running=0; staticpthread_mutex_ts_mutex; staticpthread_cond_ts_cond; staticints_cnt=0; void*thread1_fun(void*arg) { s_thread1_running=1; while(s_thread1_running) { pthread_mutex_lock(&s_mutex);///
編譯、運(yùn)行:
審核編輯:劉清
-
Linux
+關(guān)注
關(guān)注
87文章
11207瀏覽量
208721 -
C語言
+關(guān)注
關(guān)注
180文章
7594瀏覽量
135861 -
編程
+關(guān)注
關(guān)注
88文章
3565瀏覽量
93536 -
gcc編譯器
+關(guān)注
關(guān)注
0文章
78瀏覽量
3346
原文標(biāo)題:Hello系列 | 多線程編程基礎(chǔ)!
文章出處:【微信號:Linux大陸,微信公眾號:Linux大陸】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論