4. 線程的屬性
前面還說到過線程創建的時候是有屬性的,這個屬性由一個線程屬性對象來描述。線程屬性對象由pthread_attr_init()接口初始化,并由pthread_attr_destory()來銷毀,它們的完整定義是:
int?pthread_attr_init(pthread_attr_t?*attr);
int?pthread_attr_destory(pthread_attr_t?*attr);
那么線程擁有哪些屬性呢?一般地,Linux下的線程有:綁定屬性、分離屬性、調度屬性、堆棧大小屬性和滿占警戒區大小屬性。下面我們就分別來介紹這些屬性。
4.1 綁定屬性
說到這個綁定屬性,就不得不提起另外一個概念:輕進程(Light Weight Process,簡稱LWP)。輕進程和Linux系統的內核線程擁有相同的概念,屬于內核的調度實體。一個輕進程可以控制一個或多個線程。默認情況下,對于一個擁有n個線程的程序,啟動多少輕進程,由哪些輕進程來控制哪些線程由操作系統來控制,這種狀態被稱為非綁定的。那么綁定的含義就很好理解了,只要指定了某個線程“綁”在某個輕進程上,就可以稱之為綁定的了。被綁定的線程具有較高的相應速度,因為操作系統的調度主體是輕進程,綁定線程可以保證在需要的時候它總有一個輕進程可用。綁定屬性就是干這個用的。
設置綁定屬性的接口是pthread_attr_setscope(),它的完整定義是:
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
它有兩個參數,第一個就是線程屬性對象的指針,第二個就是綁定類型,擁有兩個取值:PTHREAD_SCOPE_SYSTEM(綁定的)和PTHREAD_SCOPE_PROCESS(非綁定的)。代碼2演示了這個屬性的使用。
#include?
#include?
……
int?main(?int?argc,?char?*argv[]?)
{
pthread_attr_t?attr;
pthread_t?th;
……
pthread_attr_init(?&attr?);
pthread_attr_setscope(?&attr,?PTHREAD_SCOPE_SYSTEM?);
pthread_create(?&th,?&attr,?thread,?NULL?);
……
}
代碼2設置線程綁定屬性
不知道你是否在這里發現了本文的矛盾之處。就是這個綁定屬性跟我們之前說的NPTL有矛盾之處。在介紹NPTL的時候就說過業界有一種m:n的線程方案,就跟這個綁定屬性有關。但是筆者還說過NPTL因為Linux的“蠢”沒有采取這種方案,而是采用了“1:1”的方案。這也就是說,Linux的線程永遠都是綁定。對,Linux的線程永遠都是綁定的,所以PTHREAD_SCOPE_PROCESS在Linux中不管用,而且會返回ENOTSUP錯誤。
既然Linux并不支持線程的非綁定,為什么還要提供這個接口呢?答案就是兼容!因為Linux的NTPL是號稱POSIX標準兼容的,而綁定屬性正是POSIX標準所要求的,所以提供了這個接口。如果讀者們只是在Linux下編寫多線程程序,可以完全忽略這個屬性。如果哪天你遇到了支持這種特性的系統,別忘了我曾經跟你說起過這玩意兒:)
4.2 分離屬性
前面說過線程能夠被合并和分離,分離屬性就是讓線程在創建之前就決定它應該是分離的。如果設置了這個屬性,就沒有必要調用pthread_join()或pthread_detach()來回收線程資源了。
設置分離屬性的接口是pthread_attr_setdetachstate(),它的完整定義是:
pthread_attr_setdetachstat(pthread_attr_t?*attr,?int?detachstate);
它的第二個參數有兩個取值:PTHREAD_CREATE_DETACHED(分離的)和PTHREAD_CREATE_JOINABLE(可合并的,也是默認屬性)。代碼3演示了這個屬性的使用。
#include?
#include?
……
int?main(?int?argc,?char?*argv[]?)
{
pthread_attr_t?attr;
pthread_t?th;
……
pthread_attr_init(?&attr?);
pthread_attr_setscope(?&attr,?PTHREAD_SCOPE_SYSTEM?);
pthread_create(?&th,?&attr,?thread,?NULL?);
……
}
代碼3設置線程分離屬性
4.3 調度屬性
線程的調度屬性有三個,分別是:算法、優先級和繼承權。
Linux提供的線程調度算法有三個:輪詢、先進先出和其它。其中輪詢和先進先出調度算法是POSIX標準所規定,而其他則代表采用Linux自己認為更合適的調度算法,所以默認的調度算法也就是其它了。輪詢和先進先出調度算法都屬于實時調度算法。輪詢指的是時間片輪轉,當線程的時間片用完,系統將重新分配時間片,并將它放置在就緒隊列尾部,這樣可以保證具有相同優先級的輪詢任務獲得公平的CPU占用時間;先進先出就是先到先服務,一旦線程占用了CPU則一直運行,直到有更高優先級的線程出現或自己放棄。
設置線程調度算法的接口是pthread_attr_setschedpolicy(),它的完整定義是:
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
它的第二個參數有三個取值:SCHED_RR(輪詢)、SCHED_FIFO(先進先出)和SCHED_OTHER(其它)。
Linux的線程優先級與進程的優先級不一樣,進程優先級我們后面再說。Linux的線程優先級是從1到99的數值,數值越大代表優先級越高。而且要注意的是,只有采用SHCED_RR或SCHED_FIFO調度算法時,優先級才有效。對于采用SCHED_OTHER調度算法的線程,其優先級恒為0。
設置線程優先級的接口是pthread_attr_setschedparam(),它的完整定義是:
struct?sched_param?{
int?sched_priority;
}
int?pthread_attr_setschedparam(pthread_attr_t?*attr,?struct?sched_param?*param);
sched_param結構體的sched_priority字段就是線程的優先級了。
此外,即便采用SCHED_RR或SCHED_FIFO調度算法,線程優先級也不是隨便就能設置的。首先,進程必須是以root賬號運行的;其次,還需要放棄線程的繼承權。什么是繼承權呢?就是當創建新的線程時,新線程要繼承父線程(創建者線程)的調度屬性。如果不希望新線程繼承父線程的調度屬性,就要放棄繼承權。
設置線程繼承權的接口是pthread_attr_setinheritsched(),它的完整定義是:
int?pthread_attr_setinheritsched(pthread_attr_t?*attr,?int?inheritsched);
它的第二個參數有兩個取值:PTHREAD_INHERIT_SCHED(擁有繼承權)和PTHREAD_EXPLICIT_SCHED(放棄繼承權)。新線程在默認情況下是擁有繼承權。
代碼4能夠演示不同調度算法和不同優先級下各線程的行為,同時也展示如何修改線程的調度屬性。
#include?
#include?
#include?
#include?
#define?THREAD_COUNT?12
void?show_thread_policy(?int?threadno?)
{
int?policy;
struct?sched_param?param;
pthread_getschedparam(?pthread_self(),?&policy,??m?);
switch(?policy?){
case?SCHED_OTHER:
printf(?"SCHED_OTHER?%d\n",?threadno?);
break;
case?SCHED_RR:
printf(?"SCHDE_RR?%d\n",?threadno?);
break;
case?SCHED_FIFO:
printf(?"SCHED_FIFO?%d\n",?threadno?);
break;
default:
printf(?"UNKNOWN\n");
}
}
void*?thread(?void?*arg?)
{
int?i,?j;
long?threadno?=?(long)arg;
printf(?"thread?%d?start\n",?threadno?);
sleep(1);
show_thread_policy(?threadno?);
for(?i?=?0;?i?
for(?j?=?0;?j?
printf(?"thread?%d\n",?threadno?);
}
printf(?"thread?%d?exit\n",?threadno?);
return?NULL;
}
int?main(?int?argc,?char?*argv[]?)
{
long?i;
pthread_attr_t?attr[THREAD_COUNT];
pthread_t?pth[THREAD_COUNT];
struct?sched_param?param;
for(?i?=?0;?i?
pthread_attr_init(?&attr[i]?);
for(?i?=?0;?i?
param.sched_priority?=?10;
pthread_attr_setschedpolicy(?&attr[i],?SCHED_FIFO?);
pthread_attr_setschedparam(?&attr[i],??m?);
pthread_attr_setinheritsched(?&attr[i],?PTHREAD_EXPLICIT_SCHED?);
}
for(?i?=?THREAD_COUNT?/?2;?i?
param.sched_priority?=?20;
pthread_attr_setschedpolicy(?&attr[i],?SCHED_FIFO?);
pthread_attr_setschedparam(?&attr[i],??m?);
pthread_attr_setinheritsched(?&attr[i],?PTHREAD_EXPLICIT_SCHED?);
}
for(?i?=?0;?i?
pthread_create(?&pth[i],?&attr[i],?thread,?(void*)i?);
for(?i?=?0;?i?
pthread_join(?pth[i],?NULL?);
for(?i?=?0;?i?
pthread_attr_destroy(?&attr[i]?);
return?0;
}
代碼4設置線程調度屬性
這段代碼中含有一些沒有介紹過的接口,讀者們可以使用Linux的聯機幫助來查看它們的具體用法和作用。
4.4 堆棧大小屬性
從前面的這些例子中可以了解到,線程的主函數與程序的主函數main()有一個很相似的特性,那就是可以擁有局部變量。雖然同一個進程的線程之間是共享內存空間的,但是它的局部變量確并不共享。原因就是局部變量存儲在堆棧中,而不同的線程擁有不同的堆棧。Linux系統為每個線程默認分配了8MB的堆棧空間,如果覺得這個空間不夠用,可以通過修改線程的堆棧大小屬性進行擴容。
修改線程堆棧大小屬性的接口是pthread_attr_setstacksize(),它的完整定義為:
int?pthread_attr_setstacksize(pthread_attr_t?*attr,?size_t?stacksize);
它的第二個參數就是堆棧大小了,以字節為單位。需要注意的是,線程堆棧不能小于16KB,而且盡量按4KB(32位系統)或2MB(64位系統)的整數倍分配,也就是內存頁面大小的整數倍。此外,修改線程堆棧大小是有風險的,如果你不清楚你在做什么,最好別動它(其實我很后悔把這么危險的東西告訴了你:)。
4.5 滿棧警戒區屬性
既然線程是有堆棧的,而且還有大小限制,那么就一定會出現將堆棧用滿的情況。線程的堆棧用滿是非常危險的事情,因為這可能會導致對內核空間的破壞,一旦被有心人士所利用,后果也不堪設想。為了防治這類事情的發生,Linux為線程堆棧設置了一個滿棧警戒區。這個區域一般就是一個頁面,屬于線程堆棧的一個擴展區域。一旦有代碼訪問了這個區域,就會發出SIGSEGV信號進行通知。
雖然滿棧警戒區可以起到安全作用,但是也有弊病,就是會白白浪費掉內存空間,對于內存緊張的系統會使系統變得很慢。所有就有了關閉這個警戒區的需求。同時,如果我們修改了線程堆棧的大小,那么系統會認為我們會自己管理堆棧,也會將警戒區取消掉,如果有需要就要開啟它。
修改滿棧警戒區屬性的接口是pthread_attr_setguardsize(),它的完整定義為:
int?pthread_attr_setguardsize(pthread_attr_t?*attr,?size_t?guardsize);
它的第二個參數就是警戒區大小了,以字節為單位。與設置線程堆棧大小屬性相仿,應該盡量按照4KB或2MB的整數倍來分配。當設置警戒區大小為0時,就關閉了這個警戒區。
雖然棧滿警戒區需要浪費掉一點內存,但是能夠極大的提高安全性,所以這點損失是值得的。而且一旦修改了線程堆棧的大小,一定要記得同時設置這個警戒區。
五、線程終止
pthread_exit函數:
原型: void pthread_exit(void *rval_ptr);
頭文件:
參數: rval_ptr是一個無類型指針, 指向線程的返回值存儲變量.
pthread_join函數:
原型: int pthread_join(pthread_t thread, void **rval_ptr);
頭文件:
返回值: 成功則返回0, 否則返回錯誤編號.
參數:
thread: 線程ID.
rval_ptr: 指向返回值的指針(返回值也是個指針).
說明:
調用線程將一直阻塞, 直到指定的線程調用pthread_exit, 從啟動例程返回或被取消.
如果線程從它的啟動例程返回, rval_ptr包含返回碼.
如果線程被取消, 由rval_ptr指定的內存單元置為: PTHREAD_CANCELED.
如果對返回值不關心, 可把rval_ptr設為NULL.
實例:
1 #include
2 #include
3
4 /*print process and thread IDs*/
5 void printids(const char *s)6 {7 pid_t pid, ppid;8 pthread_t tid;9 pid=getpid();10 ppid =getppid();11 tid =pthread_self();12 printf("%16s pid %5u ppid %5u tid %16u (0x%x)",13 s, (unsigned int)pid, (unsigned int)ppid,14 (unsigned int)tid, (unsigned int)tid);15 }16 /*thread process*/
17 void *thread_func(void *arg);18 {19 printids("new thread:");20 return (void *)108;21 }22 /*main func*/
23 intmain()24 {25 interr;26 void *tret; /*thread return value*/
27 pthread_t ntid;28 err = pthread_create(&ntid, NULL, thread_func, NULL);29 if (err != 0)30 perror("can't create thread");31
32 err = pthread_join(ntid, &tret);33 if (err != 0)34 perror("can't join thread");35 printids("main thread:");36 printf("thread exit code: %d", (int)tret);37 sleep(1);38 return 0;39 }
pthread_cancel函數:
pthread_cancel函數發送終止信號
pthread_setcancelstate函數設置終止方式
pthread_testcancel函數取消線程(另一功能是:設置取消點)
1) 線程取消的定義
一般情況下,線程在其主體函數退出的時候會自動終止,但同時也可以因為接收到另一個線程發來的終止(取消)請求而強制終止。
2) 線程取消的語義
線程取消的方法是向目標線程發Cancel信號(pthread_cancel函數發送Cancel信號),但如何處理Cancel信號則由目標線程自己決定,或者忽略、或者立即終止、或者繼續運行至Cancelation-point(取消點),由不同的Cancelation狀態(pthread_setcancelstate函數設置狀態)決定。
線程接收到CANCEL信號的缺省處理(即pthread_create()創建線程的缺省狀態)是繼續運行至取消點,也就是說設置一個CANCELED狀態,線程繼續運行,只有運行至Cancelation-point的時候才會退出。
3 )取消點
根據POSIX標準,pthread_join()、pthread_testcancel()、pthread_cond_wait()、?pthread_cond_timedwait()、sem_wait()、sigwait()等函數以及read()、write()等會引起阻塞的系統調用都是Cancelation-point,而其他pthread函數都不會引起Cancelation動作。但是pthread_cancel的手冊頁聲稱,由于LinuxThread庫與C庫結合得不好,因而目前C庫函數都不是Cancelation-point;但CANCEL信號會使線程從阻塞的系統調用中退出,并置EINTR錯誤碼,因此可以在需要作為Cancelation-point的系統調用前后調用?pthread_testcancel(),從而達到POSIX標準所要求的目標,即如下代碼段:
pthread_testcancel();
retcode?=?read(fd,?buffer,?length);
pthread_testcancel();
4 )程序設計方面的考慮
如果線程處于無限循環中,且循環體內沒有執行至取消點的必然路徑,則線程無法由外部其他線程的取消請求而終止。因此在這樣的循環體的必經路徑上應該加入pthread_testcancel()調用。
5 )與線程取消相關的pthread函數
int?pthread_cancel(pthread_t?thread)
發送終止信號給thread線程,如果成功則返回0,否則為非0值。發送成功并不意味著thread會終止。
int?pthread_setcancelstate(int?state,?int?*oldstate)
設置本線程對Cancel信號的反應,state有兩種值:PTHREAD_CANCEL_ENABLE(缺省)和?PTHREAD_CANCEL_DISABLE,分別表示收到信號后設為CANCLED狀態和忽略CANCEL信號繼續運行;old_state如果不為?NULL則存入原來的Cancel狀態以便恢復。
int?pthread_setcanceltype(int?type,?int?*oldtype)
設置本線程取消動作的執行時機,type由兩種取值:PTHREAD_CANCEL_DEFFERED和?PTHREAD_CANCEL_ASYCHRONOUS,僅當Cancel狀態為Enable時有效,分別表示收到信號后繼續運行至下一個取消點再退出和立即執行取消動作(退出);oldtype如果不為NULL則存入運來的取消動作類型值。
void?pthread_testcancel(void)
功能一:設置取消點;
功能二:檢查本線程是否處于Canceld狀態,如果是,則進行取消動作,否則直接返回。
代碼:
1 #include
2 #include
3 #include
4 #include
5 #include
6
7
8 #define THREAD_MAX 4
9
10
11 pthread_mutex_t mutex;12 pthread_t thread[THREAD_MAX];13
14
15 static inttries;16 static intstarted;17
18
19 void print_it(int *arg)20 {21 pthread_t tid;22 tid =pthread_self();23 printf("Thread %lx was canceled on its %d try.\n",tid,*arg);24 }25
26
27 void *Search_Num(intarg)28 {29 pthread_t tid;30 intnum;31 int k=0,h=0,j;32 intntries;33 tid =pthread_self();34
35 /*while(pthread_mutex_trylock(&mutex) == EBUSY)36 {37 printf("**************busy****************\n");38 pthread_testcancel();39 }*/
40 srand(arg);41 num = rand()&0xFFFFFF;42 //pthread_mutex_unlock(&mutex);
43
44 printf("thread num %lx\n",tid);45
46 ntries = 0;47 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);48 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);49
50 pthread_cleanup_push((void *)print_it,(void *)&ntries);51
52 while(1)53 {54 num = (num+1)&0xffffff;55 ntries++;56
57 if(arg ==num)58 {59 //只允許一個線程操作此處
60 while(pthread_mutex_trylock(&mutex) ==EBUSY) {61 //一個線程操作后其余線程進入次循環掛起,等待pthread_cancel函數發送cancel信號終止線程
62 k++;63 if(k == 10000)64 {65 printf("----------2busy2-----------\n");66 }67
68 pthread_testcancel();69 }70 tries =ntries;71 //pthread_mutex_unlock(&mutex);//如果加上這句話,將會有好幾個線程找到主函數中設定的值pid
72 printf("Thread %lx found the number!\n",tid);73
74 for(j = 0;j
82 break;83 }84 if(ntries%100 == 0)85 {86 h++;87 /*線程阻塞,其他線程爭奪資源,或者是等待pthread_cancel函數發送cancel信號終止線程*/
88 pthread_testcancel();89 /*這是為了弄明白pthread_testcancel函數的作用而設置的代碼段*/
90 if(h == 10000)91 {92 h = 0;93 printf("----------thread num %lx-------------\n",tid);94 }95 }96 }97 pthread_cleanup_pop(0);98 return (void *)0;99 }100
101
102 intmain()103 {104 inti,pid;105
106 pid = getpid(); //設置要查找的數
107
108 pthread_mutex_init(&mutex,NULL);109 printf("Search the num of %d\n",pid);110 for(started = 0; started < THREAD_MAX; started++)111 {112 pthread_create(&thread[started],NULL,(void *)Search_Num,(void *)pid);113 }114
115 for(i = 0; i < THREAD_MAX; i++)116 {117 printf("-----------i = %d--------------\n",i);118 pthread_join(thread[i],NULL);119 }120 printf("It took %d tries ot find the number!\n",tries);121 return 0;122 }123 運行結果:124 Search the num of 6531
125 -----------i = 0--------------
126 thread num b6fbcb70127 thread num b67bbb70128 thread num b5fbab70129 thread num b77bdb70130 ----------thread num b67bbb70-------------
131 Thread b67bbb70 found the number!
132 ----------thread num b6fbcb70-------------
133 ----------thread num b77bdb70-------------
134 ----------2busy2-----------
135 ----------thread num b5fbab70-------------
136 ----------2busy2-----------
137 Thread b5fbab70 was canceled on its 1174527 try.138 Thread b77bdb70 was canceled on its 1023100 try.139 -----------i = 1--------------
140 Thread b6fbcb70 was canceled on its 1174527 try.141 -----------i = 2--------------
142 -----------i = 3--------------
143 It took 1174527 tries ot find the number!
void pthread_cleanup_push(void (*rtn)(void *),void *arg);
void pthread_cleanup_pop(int execute);
當線程執行以下動作時調用清理函數,調用參數為arg,清理函數rtn的調用順序是由pthread_cleanup_push函數來安排的。
1.調用pthread_exit時。2.響應取消請求時。3.用非零execute參數調用pthread_cleanup_pop時。
如果execute參數置為0,清理函數將不被調用。無論哪種情況,pthread_cleanup_pop都將刪除上次pthread_clean_push調用建立的清理處理程序。
實例:
1 #include
2 #include
3 #include
4
5 void cleanup(void *arg)6 {7 printf("cleanup: %s\n", (char *)arg);8 }9
10 void *thr_fn1(void *arg)11 {12 printf("thread 1 start\n");13 pthread_cleanup_push(cleanup, "thread 1 first handler");14 pthread_cleanup_push(cleanup, "thread 1 second handler");15 printf("thread 1 push complete\n");16 if(arg)17 return((void *)1);18 //pthread_exit((void *)2);
19
20 pthread_cleanup_pop(0);21 pthread_cleanup_pop(0);22 //return((void *)1);
23 pthread_exit((void *)2);24
25 }26
27 void *thr_fn2(void *arg)28 {29 printf("thread 2 start\n");30 pthread_cleanup_push(cleanup, "thread 2 first handler");31 pthread_cleanup_push(cleanup, "thread 2 second handler");32 printf("thread 2 push complete\n");33 if(arg)34 pthread_exit((void *)2);35 pthread_cleanup_pop(0);36 pthread_cleanup_pop(0);37 pthread_exit((void *)2);38 }39
40 int main(void)41 {42 interr;43 pthread_t tid1, tid2;44 void *tret;45
46 err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);47 if (err != 0)48 printf("can't create thread 1: %c\n", strerror(err));49 err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);50 if (err != 0)51 printf("can't create thread 2: %c\n", strerror(err));52 err = pthread_join(tid1, &tret);53 if (err != 0)54 printf("can't join with thread 1: %c\n", strerror(err));55 printf("thread 1 exit code %d\n", (int)tret);56 err = pthread_join(tid2, &tret);57 if (err != 0)58 printf("can't join with thread 2: %c\n", strerror(err));59 printf("thread 2 exit code %d\n", (int)tret);60 exit(0);61 }62
pthread_detach()函數:
創建一個線程默認的狀態是joinable。
如果一個線程結束運行但沒有被join,則它的狀態類似于進程中的Zombie Process,即還有一部分資源沒有被回收(退出狀態碼).
所以創建線程者應該調用pthread_join來等待線程運行結束,并可得到線程的退出代 碼,回收其資源(類似于wait,waitpid) 。
但是調用pthread_join(pthread_id)后,如果該線程沒有運行結束,調用者會被阻塞,在有些情況下我們并不希望如此。
比如在Web服務器中當主線程為每個新來的鏈接創建一個子線程進行處理的時候,主線程并不希望因為調用pthread_join而阻塞(因為還要繼續處理之后到來的鏈接),這時可以
1)在子線程中加入代碼pthread_detach(pthread_self())
2)父線程調用pthread_detach(thread_id)(非阻塞,可立即返回)
這將該子線程的狀態設置為detached,則該線程運行結束后會自動釋放所有資源。
pthread_kill()函數:
pthread_kill與kill有區別,是向線程發送signal。,大部分signal的默認動作是終止進程的運行,所以,我們才要用signal()去抓信號并加上處理函數。
int pthread_kill(pthread_t thread, int sig);
向指定ID的線程發送sig信號,如果線程代碼內不做處理,則按照信號默認的行為影響整個進程,也就是說,如果你給一個線程發送了SIGQUIT,但線程卻沒有實現signal處理函數,則整個進程退出。
pthread_kill(threadid, SIGKILL)殺死整個進程。 如果要獲得正確的行為,就需要在線程內實現signal(SIGKILL,sig_handler)。所以,如果int sig的參數不是0,那一定要清楚到底要干什么,而且一定要實現線程的信號處理函數,否則,就會影響整個進程。
如果int sig是0呢,這是一個保留信號,一個作用是用來判斷線程是不是還活著。pthread_kill的返回值: 成功:0 線程不存在:ESRCH 信號不合法:EINVAL
代碼:
int kill_rc = pthread_kill(thread_id,0);
if(kill_rc == ESRCH)
printf("the specified thread did not exists or already quit\n");
else if(kill_rc == EINVAL)
printf("signal is invalid\n");
else
printf("the specified thread is alive\n");
這里附上線程基本函數:
------------------------------------------------------------------------------------------
POSIX函數????????????????????????????????????????????????????描述
-------------------------------------------------------------------------------------------
pthread_create??????????????????????????????? ??? 創建一個線程
pthread_self???????????????????????????????????? ?? 找出自己的線程ID
pthread_equal????????????????????????????????? ?? 測試2個線程ID是否相等
pthread_detach????????????????????????????????? ?設置線程以釋放資源
pthread_join??????????????????????????????????????? 等待一個線程
pthread_cancel????????????????????????????????? ? 終止另一個線程
pthread_exit??????????????????????????????????????? 退出線程,而不退出進程
pthread_kill???????????????????????????????????????? 向線程發送一個信號
-------------------------------------------------------------------------------------------
線程屬性pthread_attr_t簡介
Posix線程中的線程屬性pthread_attr_t主要包括scope屬性、detach屬性、堆棧地址、堆棧大小、優先級。在pthread_create中,把第二個參數設置為NULL的話,將采用默認的屬性配置。
pthread_attr_t的主要屬性的意義如下:
__detachstate,表示新線程是否與進程中其他線程脫離同步,?如果設置為PTHREAD_CREATE_DETACHED?則新線程不能用pthread_join()來同步,且在退出時自行釋放所占用的資源。缺省為PTHREAD_CREATE_JOINABLE狀態。這個屬性也可以在線程創建并運行以后用pthread_detach()來設置,而一旦設置為PTHREAD_CREATE_DETACH狀態(不論是創建時設置還是運行時設置)則不能再恢復到PTHREAD_CREATE_JOINABLE狀態。
__schedpolicy,表示新線程的調度策略,主要包括SCHED_OTHER(正常、非實時)、SCHED_RR(實時、輪轉法)和SCHED_FIFO(實時、先入先出)三種,缺省為SCHED_OTHER,后兩種調度策略僅對超級用戶有效。運行時可以用過pthread_setschedparam()來改變。
__schedparam,一個struct sched_param結構,目前僅有一個sched_priority整型變量表示線程的運行優先級。這個參數僅當調度策略為實時(即SCHED_RR或SCHED_FIFO)時才有效,并可以在運行時通過pthread_setschedparam()函數來改變,缺省為0。
__inheritsched,有兩種值可供選擇:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新線程使用顯式指定調度策略和調度參數(即attr中的值),而后者表示繼承調用者線程的值。缺省為PTHREAD_EXPLICIT_SCHED。
__scope,表示線程間競爭CPU的范圍,也就是說線程優先級的有效范圍。POSIX的標準中定義了兩個值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示與系統中所有線程一起競爭CPU時間,后者表示僅與同進程中的線程競爭CPU。目前LinuxThreads僅實現了PTHREAD_SCOPE_SYSTEM一值。
為了設置這些屬性,POSIX定義了一系列屬性設置函數,包括pthread_attr_init()、pthread_attr_destroy()和與各個屬性相關的pthread_attr_getXXX/pthread_attr_setXXX函數。
在設置線程屬性?pthread_attr_t?之前,通常先調用pthread_attr_init來初始化,之后來調用相應的屬性設置函數。
主要的函數如下:
1、pthread_attr_init
功能: ? ? ? ?對線程屬性變量的初始化。
頭文件: ? ?
函數原型: ? int pthread_attr_init (pthread_attr_t* attr);
函數傳入值:attr:線程屬性。
函數返回值:成功: 0
失敗: -1
2、pthread_attr_setscope
功能: ? ? ? ?設置線程?__scope?屬性。scope屬性表示線程間競爭CPU的范圍,也就是說線程優先級的有效范圍。POSIX的標準中定義了兩個值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示與系統中所有線程一起競爭CPU時間,后者表示僅與同進程中的線程競爭CPU。默認為PTHREAD_SCOPE_PROCESS。目前LinuxThreads僅實現了PTHREAD_SCOPE_SYSTEM一值。
頭文件: ? ?
函數原型: ??int?pthread_attr_setscope?(pthread_attr_t* attr, int scope);
函數傳入值:attr: 線程屬性。
scope:PTHREAD_SCOPE_SYSTEM,表示與系統中所有線程一起競爭CPU時間,
PTHREAD_SCOPE_PROCESS,表示僅與同進程中的線程競爭CPU
函數返回值得:同1。
3、pthread_attr_setdetachstate
功能: ? ? ? ?設置線程detachstate屬性。該表示新線程是否與進程中其他線程脫離同步,如果設置為PTHREAD_CREATE_DETACHED則新線程不能用pthread_join()來同步,且在退出時自行釋放所占用的資源。缺省為PTHREAD_CREATE_JOINABLE狀態。這個屬性也可以在線程創建并運行以后用pthread_detach()來設置,而一旦設置為PTHREAD_CREATE_DETACH狀態(不論是創建時設置還是運行時設置)則不能再恢復到PTHREAD_CREATE_JOINABLE狀態。
頭文件: ? ? ?
函數原型: ???int?pthread_attr_setdetachstate?(pthread_attr_t* attr, int detachstate);
函數傳入值:attr:線程屬性。
detachstate:PTHREAD_CREATE_DETACHED,不能用pthread_join()來同步,且在退出時自行釋放所占用的資源
PTHREAD_CREATE_JOINABLE,能用pthread_join()來同步
函數返回值得:同1。
4、pthread_attr_setschedparam
功能: ? ? ? 設置線程schedparam屬性,即調用的優先級。
頭文件: ? ?
函數原型: ? int?pthread_attr_setschedparam?(pthread_attr_t* attr, struct sched_param* param);
函數傳入值:attr:線程屬性。
param:線程優先級。一個struct sched_param結構,目前僅有一個sched_priority整型變量表示線程的運行優先級。這個參數僅當調度策略為實時(即SCHED_RR或SCHED_FIFO)時才有效,并可以在運行時通過pthread_setschedparam()函數來改變,缺省為0
函數返回值:同1。
5、pthread_attr_getschedparam
功能: ? ? ? 得到線程優先級。
頭文件: ? ?
函數原型: ?int?pthread_attr_getschedparam?(pthread_attr_t* attr, struct sched_param* param);
函數傳入值:attr:線程屬性;
param:線程優先級;
函數返回值:同1。
例:
1 #include
2 #include
3 #include
4 #include
5 static void pthread_func_1 (void);6 static void pthread_func_2 (void);7
8 int main (int argc, char**argv)9 {10 pthread_t pt_1 = 0;11 pthread_t pt_2 = 0;12 pthread_attr_t atrr = {0};13 int ret = 0;14
15 /*初始化屬性線程屬性*/
16 pthread_attr_init (&attr);17 pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);18 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);19
20 ret = pthread_create (&pt_1, &attr, pthread_func_1, NULL);21 if (ret != 0)22 {23 perror ("pthread_1_create");24 }25
26 ret = pthread_create (&pt_2, NULL, pthread_func_2, NULL);27 if (ret != 0)28 {29 perror ("pthread_2_create");30 }31
32 pthread_join (pt_2, NULL);33
34 return 0;35 }36
37 static void pthread_func_1 (void)38 {39 int i = 0;40
41 for (; i < 6; i++)42 {43 printf ("This is pthread_1.\n");44
45 if (i == 2)46 {47 pthread_exit (0);48 }49 }50
51 return;52 }53
54 static void pthread_func_2 (void)55 {56 int i = 0;57
58 for (; i < 3; i ++)59 {60 printf ("This is pthread_2.\n");61 }62
63 return;64 }