Linux系统编程---13(线程控制函数,创建线程,循环创建多个线程,线程间共享全局变量)
線程控制
操作系統(tǒng)并沒(méi)有提供創(chuàng)建線程的系統(tǒng)調(diào)用接口,因此大佬們封裝了一個(gè)線程的接口庫(kù)實(shí)現(xiàn)線程控制。意為著用戶創(chuàng)建線程都使用的是庫(kù)函數(shù)(所以有時(shí)候我們說(shuō)創(chuàng)建的線程是一個(gè)用戶態(tài)線程,但是在內(nèi)核中對(duì)應(yīng)有一個(gè)輕量級(jí)進(jìn)程實(shí)現(xiàn)線程程序的調(diào)度)
進(jìn)程ID == 線程組id–tgid == 主線程pid
線程控制函數(shù)
pthread_self 函數(shù)
獲取線程 ID。其作用對(duì)應(yīng)進(jìn)程中 getpid() 函數(shù)。
pthread_t pthread_self(void); 返回值:成功:0; 失敗:無(wú)!
線程 ID:pthread_t 類型,本質(zhì):在 Linux 下為無(wú)符號(hào)整數(shù)(%lu),其他系統(tǒng)中可能是結(jié)構(gòu)體實(shí)現(xiàn)
線程 ID 是進(jìn)程內(nèi)部,識(shí)別標(biāo)志。(兩個(gè)進(jìn)程間,線程 ID 允許相同)
注意:不應(yīng)使用全局變量 pthread_t tid,在子線程中通過(guò) pthread_create 傳出參數(shù)來(lái)獲取線程 ID,而應(yīng)使用 pthread_self。
pthread_create 函數(shù)
創(chuàng)建一個(gè)新線程。 其作用,對(duì)應(yīng)進(jìn)程中 fork() 函數(shù)。
int pthread_create (pthread_t * thread,const pthread_attr_t *attr,void* (*start_routine)(void*),void* arg);
返回值:成功:0; 失敗:錯(cuò)誤號(hào) -----Linux 環(huán)境下,所有線程特點(diǎn),失敗均直接返回錯(cuò)誤號(hào)。
參數(shù):
pthread_t:當(dāng)前 Linux 中可理解為:typedef unsigned long int pthread_t;
參數(shù) 1:傳出參數(shù),保存系統(tǒng)為我們分配好的線程 ID
參數(shù) 2:通常傳 NULL,表示使用線程默認(rèn)屬性。若想使用具體屬性也可以修改該參數(shù)。
參數(shù) 3:函數(shù)指針,指向線程主函數(shù)(線程體),該函數(shù)運(yùn)行結(jié)束,則線程結(jié)束。
參數(shù) 4:線程主函數(shù)執(zhí)行期間所使用的參數(shù)。
在一個(gè)線程中調(diào)用 pthread_create()創(chuàng)建新的線程后,當(dāng)前線程從 pthread_create()返回繼續(xù)往下執(zhí)行,而新的線 程所執(zhí)行的代碼由我們傳給 pthread_create 的函數(shù)指針 start_routine 決定。
start_routine 函數(shù)接收一個(gè)參數(shù),是通過(guò) pthread_create 的 arg 參數(shù)傳遞給它的,該參數(shù)的類型為 void *,這個(gè)指針按什么類型解釋由調(diào)用者自己定義。
start_routine 的返回值類型也是 void*,這個(gè)指針的含義同樣由調(diào)用者自己定義。start_routine 返回時(shí),這個(gè)線程就 退出了,其它線程可以調(diào)用 pthread_join 得到 start_routine 的返回值,類似于父進(jìn)程調(diào)用 wait(2)得到子進(jìn)程的退出 狀態(tài),稍后詳細(xì)介紹 pthread_join。
pthread_create 成功返回后,新創(chuàng)建的線程的 id 被填寫到 thread 參數(shù)所指向的內(nèi)存單元。我們知道進(jìn)程 id 的類 型是 pid_t,每個(gè)進(jìn)程的 id 在整個(gè)系統(tǒng)中是唯一的,調(diào)用 getpid(2)可以獲得當(dāng)前進(jìn)程的 id,是一個(gè)正整數(shù)值。
線程 id 的類型是 thread_t,它只在當(dāng)前進(jìn)程中保證是唯一的,在不同的系統(tǒng)中 thread_t 這個(gè)類型有不同的實(shí)現(xiàn),它可能 是一個(gè)整數(shù)值,也可能是一個(gè)結(jié)構(gòu)體,也可能是一個(gè)地址,所以不能簡(jiǎn)單地當(dāng)成整數(shù)用printf打印,調(diào)用pthread_self(3) 可以獲得當(dāng)前線程的 id。
pthread_t到底是什么類型呢?取決于實(shí)現(xiàn)。對(duì)于Linux目前實(shí)現(xiàn)的NPTL實(shí)現(xiàn)而言,pthread_t類型的線程ID,本質(zhì)就 是一個(gè)進(jìn)程地址空間上的一個(gè)地址。線程空間的首地址
練習(xí):創(chuàng)建一個(gè)新線程,打印線程 ID。注意:鏈接線程庫(kù) -lpthread
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<pthread.h>void *thrd_func(void *arg) {//子線程的IDprintf(" In thread id = %lu,pid = %u\n",pthread_self(),getpid());return NULL; }int main(void) {pthread_t tid;int ret;//主控線程IDprintf(" In main1 id = %lu,pid = %u\n",pthread_self(),getpid());ret = pthread_create(&tid,NULL,thrd_func,NULL);if(ret != 0){ printf("pthread_create error\n");exit(1);} printf(" In main2 id = %lu,pid = %u\n",pthread_self(),getpid()); sleep(1);//主控線程等待子線程1秒,讓它打印return 0; }
思考:
由于 pthread_create 的錯(cuò)誤碼不保存在 errno 中,因此不能直接用 perror(3)打印錯(cuò)誤信息,可以先用 strerror(3) 把錯(cuò)誤碼轉(zhuǎn)換成錯(cuò)誤信息再打印。如果任意一個(gè)線程調(diào)用了 exit 或_exit,則整個(gè)進(jìn)程的所有線程都終止,由于從 main 函數(shù) return 也相當(dāng)于調(diào)用 exit,為了防止新創(chuàng)建的線程還沒(méi)有得到執(zhí)行就終止,我們?cè)?main 函數(shù) return 之前 延時(shí) 1 秒,這只是一種權(quán)宜之計(jì),即使主線程等待 1 秒,內(nèi)核也不一定會(huì)調(diào)度新創(chuàng)建的線程執(zhí)行。
循環(huán)創(chuàng)建多個(gè)線程,每個(gè)線程打印自己是第幾個(gè)被創(chuàng)建的線程。(類似于進(jìn)程循環(huán)創(chuàng)建子進(jìn)程)
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<pthread.h>void *thrd_func(void *arg) {int i=(int)arg;sleep(i); //子線程的IDprintf("%d th In thread id = %lu,pid = %u\n", i+1 ,pthread_self(),getpid());return NULL; }int main(void) {pthread_t tid;int ret,i;for(i=0;i < 5; i++){ ret = pthread_create(&tid,NULL,thrd_func,(void *)i);if(ret != 0){ fprintf(stderr,"pthread_create error:%s\n",strerror(ret));exit(1);} } sleep(i);// 主控線程等待子線程1秒,讓它打印 return 0;// 將當(dāng)前進(jìn)程退出 }
注意:將 pthread_create 函數(shù)參 4 修改為(void*)&i, 將線程主函數(shù)內(nèi)改為 i=*((int*)arg) 不可以
線程間共享全局變量
驗(yàn)證
#include<stdio.h> #include<pthread.h> #include<stdlib.h> #include<unistd.h>int var =100;void *tfn(void * arg) {//子線程修改varvar = 200;printf("thread\n");return NULL; }int main(void) {printf("At first var = %d\n",var);pthread_t tid;pthread_create(&tid,NULL,tfn,NULL);sleep(1);//等子線程退出后,再次打印var printf("after pthread_create, var = %d\n",var);return 0; }
線程默認(rèn)共享數(shù)據(jù)段、代碼段等地址空間,常用的是全局變量。而進(jìn)程不共享全局變量,只能借助 mmap
pthread_exit 函數(shù)
將單個(gè)線程退出
void pthread_exit (void*retval); 參數(shù):retval 表示線程退出狀態(tài),通常傳 NULL
思考:使用 exit 將指定線程退出,可以嗎?
結(jié)論:線程中,禁止使用 exit 函數(shù),會(huì)導(dǎo)致進(jìn)程內(nèi)所有線程全部退出。
在不添加 sleep 控制輸出順序的情況下。pthread_create 在循環(huán)中,幾乎瞬間創(chuàng)建 5 個(gè)線程,但只有第 1 個(gè)線程 有機(jī)會(huì)輸出(或者第 2 個(gè)也有,也可能沒(méi)有,取決于內(nèi)核調(diào)度)如果第 3 個(gè)線程執(zhí)行了 exit,將整個(gè)進(jìn)程退出了, 所以全部線程退出了。
所以,多線程環(huán)境中,應(yīng)盡量少用,或者不使用 exit 函數(shù),取而代之使用 pthread_exit 函數(shù),將單個(gè)線程退出。 任何線程里 exit 導(dǎo)致進(jìn)程退出,其他線程未工作結(jié)束,主控線程退出時(shí)不能 return 或 exit。
另注意,pthread_exit 或者 return 返回的指針?biāo)赶虻膬?nèi)存單元必須是全局的或者是用 malloc 分配的,不能在 線程函數(shù)的棧上分配,因?yàn)楫?dāng)其它線程得到這個(gè)返回指針時(shí)線程函數(shù)已經(jīng)退出了。
總結(jié)
以上是生活随笔為你收集整理的Linux系统编程---13(线程控制函数,创建线程,循环创建多个线程,线程间共享全局变量)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux系统编程----12(线程概念
- 下一篇: 创世神话玉虚宫用什么法宝比较好?