線程:
一.什麼是線程(tcb)。
線程是在進程地址空間內啟動並執行,強調資源共用(多個線程共用一份地址空間)。Linux下是用進程類比線程,並無真正的線程。進程是分配資源的基本單位,線程是調度的基本單位。
二.線程可以共用和獨佔。
共用的:
1. 檔案描述符表
2. 每種訊號的處理方式(SIG_IGN、SIG_DFL或者自訂的訊號處理函數)
3. 當前工作目錄
4. 使用者id和組id
獨佔的:
1. 線程id
2. 上下文,包括各種寄存器的值,程式計數器和指標。
3. 棧空間
4. 訊號屏蔽字
5. 調度優先順序
三.線程的操作(遵守posix標準)
線程建立:
int pthread_create(pthread_t *thread, constpthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
傳回值:成功返回0,失敗返回錯誤碼
pthread_t *thread:線程id,只在使用者區有效,建立並且附過來。
pthread_attr_t *attr:一般為NULL,設定線程屬性。
void *(*start_routine) (void *):函數指標,調用後執行此函數中的代碼。
void *arg:為第三個參數的參數。一般為NULL。
線程終止:
void pthread_exit(void *retval);
參數:reval是void*類型的,和線程函數傳回值的用法一樣,其他線程可以調用pthread_join獲得這個指標。pthread_exit或return返回的指標所指向的單元必須是全域或用malloc分配的,不能線上程函數的棧上分配,因為其他線程得到這個返回指標時線程已經退出。
線程取消:
int pthread_cancel(pthread_t thread);
傳回值:成功返回0,失敗返回錯誤碼。
線程等待:
int pthread_join(pthread_t thread,void **retval);
調用該函數的線程將掛起等待,直到id為thread的線程終止。thread線程以不同的.方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:
1. 如果thread線程通過return返回,value_ptr所指向的單元裡存放的是thread線程函數的傳回值。
2. 如果thread線程被別的線程調用pthread_cancel異常終掉,value_ptr所指向的單元裡存放的是常數PTHREAD_CANCELED。
3. 如果thread線程是自己調用pthread_exit終止的.value_ptr所指向的單元存放的是傳給pthread_exit的參數。 如果對thread線程的終止狀態不感興趣,可以傳NULL給value_ptr參數。
下面驗證線程退出時的狀態
void * thread_run1(void *arg){ printf("thread_run1 : tid:%lu pid:%d \n",pthread_self(),getpid()); return ((void*)1);} void * thread_run2(void *arg){ printf("thread_run2 : tid:%lu pid:%d\n",pthread_self(),getpid()); pthread_exit((void*)2); return ((void*)1);} void * thread_run3(void *arg){ while(1) { printf("thread_run3 :tid:%lu pid:%d \n",pthread_self(),getpid()); sleep(1); } return NULL;}int main(){ pthread_t id; void *temp; //正常退出時候查看他的id以及傳回值 pthread_create(&id,NULL,thread_run1,NULL); pthread_join(id,&temp); printf("thread_run1 return:id %u return code%d\n",id,(int)temp); //當中間調用線程終止的時候 pthread_create(&id,NULL,thread_run2,NULL); pthread_join(id,&temp); printf("thread_run2 return:id %u return code%d\n",id,(int)temp); //當線程死迴圈時用函數取消線程的時候 pthread_create(&id,NULL,thread_run3,NULL); sleep(1); pthread_cancel(id); pthread_join(id,&temp); printf("thread_run3 return:id %u return code%d\n",id,(int)temp); printf("the main tid is %u pid %d\n",pthread_self(),getpid()); return 0;}
運行結果:
線程與分離與結合:
在任何一個時間點上,線程是可結合的(joinable)或者是分離的(detached)。一個可結合的線程能夠被其他線程收回其資源和殺死。在被其他線程回收之前,它的儲存空間資源(例如棧)是不釋放的。相反,一個分離的線程是不能被其他線程回收或殺死的,它的儲存空間資源在它終止時由系統自動釋放。
建立出的線程預設屬性為可結合,所以必須等待它否則會產生類似殭屍進程的情況。
若線程為可分離則不需要join,線程運行結束後會自動釋放所有資源。
驗證分離的線程是否能被join等待
void *thread_run(void * arg){ pthread_detach(pthread_self()); printf("%s\n",(char*)arg); return NULL;} int main(){ pthread_t id; int temp =pthread_create(&id,NULL,thread_run,"thread1 run\n"); if(temp != 0) { printf("create errorcode:%s\n",strerror(temp)); return -1; } //一個分離的線程是不能被其他進程殺死或回收的 所以最後回收失敗 int ret = 0; sleep(2); if(0 == pthread_join(id,NULL)) { printf("thread waitsuccess\n"); ret = 0; } else { printf("thread waitfailed\n"); ret = 1; } return ret;}
線程的同步與互斥(相當於一把鎖,二元訊號量):
初始化:pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER
上鎖:pthread_mutex_t_lock(&mutex_lock);
解鎖:pthread_mutex_t_unlock(&mutex_lock);
如果沒有加鎖與去鎖
當同時執行四個線程的時候 加出來最後的結果不一定為我們想要得到的值 因為會發生資料不一致問題。
當我們加鎖之後 數值就會一致
pthread_mutex_t mx= PTHREAD_MUTEX_INITIALIZER;static int g_count= 0;void *read_write_mem(void * _val){ int val = 0; int i = 0; for(;i<5000;++i) { pthread_mutex_lock(&mx); val = g_count; printf("pthread idis:%x,count is:%d \n",pthread_self(),g_count); g_count = val+1; pthread_mutex_unlock(&mx); }return NULL;} int main(){ pthread_t tid1; pthread_t tid2; pthread_t tid3; pthread_t tid4; pthread_create(&tid1,NULL,read_write_mem,NULL); pthread_create(&tid2,NULL,read_write_mem,NULL);pthread_create(&tid3,NULL,read_write_mem,NULL);pthread_create(&tid4,NULL,read_write_mem,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_join(tid4,NULL); printf("count val is%d\n",g_count); return 0;}