#include //新建線程 int pthread_create(pthread_t *restrict tidp, constpthread_attr_t *restrict attr,void*(*start_rtn)(void*),void *restrict arg); //線程終止 void pthread_exit(void *rval_ptr);//線程自身主動退出 int pthread_join(pthread_t tid, void **rval_ptr);//其他線程阻塞自身,等待tid退出 //線程清理 voidpthread_cleanup_push(void(*rtn)(void*), void *arg); voidpthread_cleanup_pop(intexecute); 另: 操縱函數 pthread_create():創建一個線程 pthread_exit():終止當前線程 pthread_cancel():中斷另外一個線程的運行 pthread_join():阻塞當前的線程,直到另外一個線程運行結束 pthread_attr_init():初始化線程的屬性 pthread_attr_setdetachstate():設置脫離狀態的屬性(決定這個線程在終止時是否可以被結合) pthread_attr_getdetachstate():獲取脫離狀態的屬性 pthread_attr_destroy():刪除線程的屬性 pthread_kill():向線程發送一個信號
Pthread同步函數
用於 mutex 和條件變量 pthread_mutex_init() 初始化互斥鎖 pthread_mutex_destroy() 刪除互斥鎖 pthread_mutex_lock():占有互斥鎖(阻塞操作) pthread_mutex_trylock():試圖占有互斥鎖(不阻塞操作)。即,當互斥鎖空閒時,將占有該鎖;否則,立即返回。 pthread_mutex_unlock(): 釋放互斥鎖 pthread_cond_init():初始化條件變量 pthread_cond_destroy():銷毀條件變量 pthread_cond_signal(): 喚醒第一個調用pthread_cond_wait()而進入睡眠的線程 pthread_cond_wait(): 等待條件變量的特殊條件發生 Thread-local storage(或者以Pthreads術語,稱作線程特有數據): pthread_key_create(): 分配用於標識進程中線程特定數據的鍵 pthread_setspecific(): 為指定線程特定數據鍵設置線程特定綁定 pthread_getspecific(): 獲取調用線程的鍵綁定,並將該綁定存儲在 value 指向的位置中 pthread_key_delete(): 銷毀現有線程特定數據鍵 pthread_attr_getschedparam();獲取線程優先級 pthread_attr_setschedparam();設置線程優先級
Pthread工具函數
pthread_equal(): 對兩個線程的線程標識號進行比較 pthread_detach(): 分離線程 pthread_self(): 查詢線程自身線程標識號
2、pthread_create()線程創建
函數簡介
pthread_create是UNIX環境創建線程函數。創建線程(實際上就是確定調用該線程函數的入口點),在線程創建以後,就開始運行相關的線程函數。
頭文件
#include
函數聲明
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
返回值
若成功則返回0,否則返回出錯編號,返回-1。
參數
第一個參數為指向線程標識符的指針。
第二個參數用來設置線程屬性。
第三個參數是線程運行函數的起始地址。
最後一個參數是運行函數的參數。
另外
在編譯時注意加上-lpthread參數,以調用靜態鏈接庫。因為pthread並非Linux系統的默認庫
舉例:
/*thread.c*/
#include
#include
/*線程一*/
void thread_1(void)
{
int i=0;
for(i=0;i<=6;i++)
{
printf("This is a pthread_1.\n");
if(i==2)
pthread_exit(0);
sleep(1);
}
}
/*線程二*/
void thread_2(void)
{
int i;
for(i=0;i<3;i++)
printf("This is a pthread_2.\n");
pthread_exit(0);
}
int main(void)
{
pthread_t id_1,id_2;
int i,ret;
/*創建線程一*/
ret=pthread_create(&id_1,NULL,(void *) thread_1,NULL);
if(ret!=0)
{
printf("Create pthread error!\n");
return -1;
}
/*創建線程二*/
ret=pthread_create(&id_2,NULL,(void *) thread_2,NULL);
if(ret!=0)
{
printf("Create pthread error!\n");
return -1;
}
/*等待線程結束*/
pthread_join(id_1,NULL);
pthread_join(id_2,NULL);
return 0;
}
以下是程序運行結果:
備注:pthread庫不是Linux系統默認的庫,連接時需要使用靜態庫libpthread.a,所以在線程函數在編譯時,需要連接庫函數,如上圖 gcc pthread_create.c -o pthread_create -lpthread
3、pthread_join函數
函數簡介
函數pthread_join用來等待一個線程的結束。
函數原型為:
extern int pthread_join __P (pthread_t __th, void **__thread_return);
參數:
第一個參數為被等待的線程標識符,第二個參數為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數是一個線程阻塞的函數,調用它的函數將一直等待到被等待的線程結束為止,當函數返回時,被等待線程的資源被收回。如果執行成功,將返回0,如果失敗則返回一個錯誤號。
例子:
#include
#include
#include
struct member
{
int num;
char *name;
};
//結構體後的分號勿漏
void *create(void *arg)
//有void* 型參數傳入,不能直接void
{
struct member *temp;
temp=(struct member *)arg;
//結構體變量之間不能直接賦值,但可以通過指針賦地址
printf("member->num:%d\n",temp->num);
printf("member->name:%s\n",temp->name);
sleep(1);
return (void *)8;
//這個很有特色,返回一個指向void的數據類型的值,這個值作為後面的exit code
}
int main(int agrc,char* argv[])
{
pthread_t tidp;
struct member *b;
void* a;
b=(struct member *)malloc(sizeof(struct member));
//先分配內存空間撒~
b->num=1;
b->name="mlq";
//字符串賦值,其他好用簡便的方法有: char *p = NULL; p = new char [256];
if((pthread_create(&tidp,NULL,create,(void*)b))==-1) /
//
void *
為“無類型指針”,void * 可以指向任何類型的數據
{
printf("create error!\n");
return 1;
}
4、pthread_detach()函數
pthread_detach(pthread_self())
linux線程執行和windows不同,pthread有兩種狀態joinable狀態和unjoinable狀態。創建一個線程默認的狀態是joinable。
如果線程是joinable狀態,當線程函數自己返回退出時或pthread_exit時都不會釋放線程所占用堆棧和線程描述符(總計8K多)。只有當你調用了pthread_join之後這些資源才會被釋放。但是調用pthread_join(pthread_id)後,如果該線程沒有運行結束,調用者會被阻塞,在有些情況下我們並不希望如此。
若是unjoinable狀態的線程,這些資源在線程函數退出時或pthread_exit時自動會被釋放。
unjoinable屬性可以在pthread_create時指定,或在線程創建後在線程中pthread_detach自己,如:pthread_detach(pthread_self()),將狀態改為unjoinable狀態,確保資源的釋放。或者將線程置為joinable,然後適時調用pthread_join.
其實簡單的說就是在線程函數頭加上 pthread_detach(pthread_self())的話,線程狀態改變,在函數尾部直接pthread_exit線程就會自動退出。省去了給線程擦屁股的麻煩
eg:
pthread_t tid;
int status = pthread_create(&tid, NULL, ThreadFunc, NULL);
if(status != 0)
{
perror("pthread_create error");
}
pthread_detach(tid);
如在Web服務器中當主線程為每個新來的鏈接創建一個子線程進行處理的時候,主線程並不希望因為調用pthread_join而阻塞(因為還要繼續處理之後到來的鏈接),這時可以在子線程中加入代碼
pthread_detach(pthread_self()) 或者父線程調用 pthread_detach(thread_id)(非阻塞,可立即返回) 這將該子線程的狀態設置為detached,則該線程運行結束後會自動釋放所有資源。
5、線程終止
a. 任一線程調用exit, _Exit, _exit都將導致整個進程終止;
b. 單個線程退出方式有三種:
1> 線程執行函數start_rtn()中使用return返回,返回值為線程退出碼;
2> 被同一個進程的其他線程使用pthread_cancel()取消;
3> 線程自身調用了pthread_exit();
說明:pthread_join(pthread_t tid, void **rval_ptr)函數會阻塞調用線程,直到tid線程通過上述三種方式終止退出,且return/pthread_exit()方式會設置相應線程退出碼rval_ptr,而pthread_cancel()取消的線程,將退出碼設置為PTHREAD_CANCELED.
a> pthread_cleanup_push()與pthread_cleanup_pop()均為
?
pthread_cleanup_push and pthread_cleanup_pop are macros and must always be used in matching pairs at the same nesting level of braces. */ # define pthread_cleanup_push(routine, arg) \ do{ \ __pthread_cleanup_class __clframe (routine, arg) /* Remove a cleanup handler installed by the matching pthread_cleanup_push. If EXECUTE is non-zero, the handler function is called. */ # define pthread_cleanup_pop(execute) \ __clframe.__setdoit (execute); \ }while(0)
可見push/pop中的{/}是一一對應的,因此pthread_cleanup_push/pop()也應一一對應出現,否則編譯出錯。
b> 當線程執行下列之一操作時調用清理函數,thread_cleanup_push由棧結構實現,注意清理程序調用的順序,先入後出。
1: 調用pthread_exit()時,而直接return不會出發清理函數;
2: 相應取消請求pthread_cancel()時;
3: 使用非零execute參數調用pthread_cleanup_pop()時;
尤其需注意pthread_cleanup_pop()參數不同及此語句所處位置不同而有不同效果。
看此代碼實例,注意return或pthread_exit()位置不同導致pthread_cleanup_pop()不同參數的效果變化。
?
#include void testPointerSize() { void*tret; printf("size of pointer in x86-64:%d\n",sizeof(tret)); //result is 8 in x86-64. //which is 4 in x86-32. printf("size of int in x86-64:%d\n",sizeof(int)); //result is 4 in x86-64. //which is also 4 in x86-32. } voidcleanup(void*arg) { printf("cleanup:%s\n",(char*)arg); } void* thr_fn1(void*arg) { printf("thread 1 start\n"); pthread_cleanup_push(cleanup,"thread 1 first handler"); pthread_cleanup_push(cleanup,"thread 1 second handler"); if(arg) return((void*)1);//arg !=0 ,return here. // return here will not triger any cleanup. pthread_cleanup_pop(0); pthread_cleanup_pop(1); return((void*)2);//will not run this } void* thr_fn2(void*arg) { printf("thread 2 start\n"); pthread_cleanup_push(cleanup,"thread 2 first handler"); pthread_cleanup_push(cleanup,"thread 2 second handler"); pthread_cleanup_pop(0); pthread_cleanup_pop(1); return((void*)2); // return here can triger cleanup second handler; } void* thr_fn3(void*arg) { printf("thread 3 start\n"); pthread_cleanup_push(cleanup,"thread 3 first handler"); pthread_cleanup_push(cleanup,"thread 3 second handler"); if(arg) pthread_exit((void*)3); //pthread_exit() here will triger both cleanup first&second handler. pthread_cleanup_pop(1); pthread_cleanup_pop(0); pthread_exit((void*)3);//wont run this } void* thr_fn4(void*arg) { printf("thread 4 start\n"); pthread_cleanup_push(cleanup,"thread 4 first handler"); pthread_cleanup_push(cleanup,"thread 4 second handler"); pthread_cleanup_pop(1); pthread_cleanup_pop(0); pthread_exit((void*)4); //pthread_exit() here will triger cleanup second handler. } int main(void) { testPointerSize(); interr; pthread_t tid1, tid2, tid3, tid4; void*tret; err = pthread_create(&tid1, NULL, thr_fn1, (void*)1); err = pthread_join(tid1,&tret); printf("thread 1 exit code %d\n",(int)tret); err = pthread_create(&tid2, NULL, thr_fn2, (void*)2); err = pthread_join(tid2, &tret); printf("thread 2 exit code %d\n",(int)tret); err = pthread_create(&tid3, NULL, thr_fn3, (void*)3); err = pthread_join(tid3,&tret); printf("thread 3 exit code %d\n",(int)tret); err = pthread_create(&tid4, NULL, thr_fn4, (void*)4); err = pthread_join(tid4, &tret); printf(