多線程API - 小白如何快速上手併發編程
-
線程的基本概念
- 一.線程和進程的區別(linux環境下)
- 進程是資源管理的最小單位,線程是系統執行的最小單位
- 線程和進程都有對應的PCB和TCB,在linux內核中創建線程和進程使用的底層函數和進程一樣,都是clone
- 進程可以蛻化成線程,一個進程可以有一個至多個線程
- 進程有自己獨享的0-4G虛擬地址空間,而線程共享同一進程的地址空間
- 二.線程有共享也有獨佔
- 線程共享的資源有:
- 1.文件描述符表
- 2.每種信號的處理方式
- 3.當前工作目錄
- 4.用戶ID和組ID
- 5.內存地址空間 (.text/.data/.bss/heap/共享庫)
- 線程獨佔的資源有:
- 1.線程id
- 2.計數器和棧空間(內核棧和用戶空間棧)
- 3.信號屏蔽字
- 4.errno變量
- 5.調度優先級
- 線程共享的資源有:
- 三.線程的優缺點
- 優點
- 1.提高程序併發性
- 2.開銷小
- 3.數據通信、共享數據方便
- 缺點
- 1.線程所有操作函數 pthread_* 是庫函數,而非系統調用,不夠穩定
- 2.編寫調試困難,不支持gdb
- 3.對信號支持性差
- 優點
- 一.線程和進程的區別(linux環境下)
-
使用線程的API
- 線程常見的函數有以下幾種
- pthread_self函數 - 獲取線程id
- pthread_create函數 - 創建一個新線程
- pthread_exit函數 - 將單個線程退出
- pthread_join函數 - 阻塞等待線程退出並獲取線程退出狀態
- pthread_cancel函數 - 殺死(取消)線程
- pthread_detach函數 - 實現線程分離
- pthread_equal函數 - 比較線程id是否相等(系統留用,看以後pthread_t是否改爲結構體)
- 線程API和進程API對比
- pthread_self - getpid
- pthread_create - fork
- pthread_exit - exit
- pthread_join - wait
- pthread_cancel - kill
- 線程常見的函數有以下幾種
-
線程控制原語
-
pthread_t pthread_self(void); ---------使用見線程實例1
-
功能 : 獲取線程id
-
返回值:成功:0; 失敗:NULL
-
線程ID:pthread_t類型,本質:在Linux下爲無符號整數(%lu),可以理解爲typedef unsigned long int pthread_t;
-
notes
-
線程id是進程內識別不同線程的標誌,也意味着兩個進程間有允許使用相同的線程id
-
在獲取線程id時,避免使用全局變量。在主線程可通過pthread_create第一個參數傳出,在子線程通過調用pthread_self()得到id.
-
-
-
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -----使用見線程實例1
-
功能 :創建一個新線程
-
返回值:成功:0; 失敗:錯誤號 -----Linux環境下,所有線程特點,失敗均直接返回錯誤號。
-
parameters
-
參數1:傳出參數,保存系統爲我們分配好的線程ID
-
參數2:通常傳NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。
-
參數3:函數指針,指向線程主函數(線程體),該函數運行結束,則線程結束。
-
參數4:線程主函數執行期間所使用的參數==,若線程需要多個參數,可傳入結構體變量==
-
-
-
-
void pthread_exit(void *retval); ------ 使用見線程實例3
-
功能 : 將單個線程退出
-
parameter
- retval表示線程退出狀態,通常傳NULL
-
notes
-
不能使用exit退出指定的線程,因爲exit是退出進程的,會將該進程所有線程退出
-
線程的三種退出方式
- return
- pthread_exit()
- pthread_cancel()
-
-
-
-
int pthread_join(pthread_t thread, void **retval); ------使用見線程實例4
-
功能 : 阻塞等待線程退出並獲取線程退出狀態
-
parameters
- 參數1 : 需要等待的線程id
- 參數2 : 獲取線程id退出狀態
-
notes
-
如果thread線程通過return返回,retval所指向的單元裏存放的是thread線程函數的返回值.
-
如果thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元裏存放的是常數PTHREAD_CANCELED(一般爲-1)。
-
如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳pthread_exit的參數
-
如果對線程退出狀態沒有興趣,可以對retval傳空。
-
-
-
int pthread_cancel(pthread_t thread); ------使用見線程實例4
-
功能 : 殺死(取消)線程
-
成功:0;失敗:錯誤號
-
notes
- 線程取消不是立馬執行的,需要一定的時間。需要線程進入系統調用creat,open,pause,close,read,write等,纔可退出,若線程中沒有系統調用,需要添加pthread_testcancel()函數斷點,否則線程死循環
-
-
int pthread_detach(pthread_t thread); ------使用見線程實例5
-
功能 : 實現線程分離
-
返回值:0;失敗:錯誤號
-
notes
- 分離態線程一旦終止就立刻回收它佔用的所有資源,而不保留終止狀態,避免殭屍線程。不能對一個已經處於detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL錯誤(一般爲22)。
-
-
int pthread_equal(pthread_t t1, pthread_t t2);//幾乎不用
- 功能 : 獲取線程id
-
-
線程實例
-
1.創建一個子線程,子線程打印出自己的線程id
#include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread1(void *arg) { printf("my thread_id is %lu\n",pthread_self()); return NULL; } int main(int args, char *argv[]) { pthread_t tid;//pthread is equal to unsigned long in linux pthread_create(&tid, NULL, thread1, NULL);//第一個NULL表示默認屬性,第二個是線程執行時所需要的參數 sleep(1);//wait thread end return 0; }
- 編譯
- 運行
-
2.循環創建多個子線程,並使用pthread_exit退出其中i = 3的線程
#include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread(void *arg) { int i = 0; i = (int)arg; if(3 == i)//3 thread exit { pthread_exit(NULL); } sleep(i);//keep thread write order printf("I am %d thead, my thread id is %lu\n", i, pthread_self()); } int main(int args, char *arg[]) { pthread_t tid = 0; int i = 0; for(i=0; i<5; i++) { pthread_create(&tid, NULL, thread, (void*)i); } sleep(i);//wait thread end return 0; }
- 編譯
- 運行
-
3.創建多個線程並使用pthread_exit退出,打印出join保存的線程退出狀態
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> typedef struct node { int a; int b; }LNODE; void *thread(void *arg) { LNODE *node1; node1 = (LNODE*)malloc(sizeof(struct node)); if(NULL == node1) { printf("no enough memory malloc\n "); return NULL; } node1->a = 100; node1->b = 200; pthread_exit((void *)node1);//如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數 } int main(int args,char *argv[]) { pthread_t tid = 0; LNODE *node2 = NULL; pthread_create(&tid, NULL, thread, NULL); pthread_join(tid, (void**)&node2); printf("node->a is %d, node->b = %d\n", node2->a, node2->b); free(node2); node2 = NULL; return 0; }
- 編譯
- 運行
-
4.創建三個進程,分佈使用不同的退出方式進行測試(return, pthread_exit, pthread_cancel)
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int *thread1(void *arg)//return { printf("I am thread1, my tid is %lu\n", pthread_self()); return 111; } void *thread2(void *arg)//pthread { printf("I am thread2, my tid is %lu\n", pthread_self()); pthread_exit((void *)222); } void *thread3(void *arg)//cancel { int i = 0; while(1) { i++; pthread_testcancel(); } return (void *)333; } int main(int args, char *argv[]) { pthread_t tid = 0; void *pointer = NULL; pthread_create(&tid, NULL, (void *)thread1, NULL); pthread_join(tid, &pointer); printf("thread1 return value is %d\n", (int)pointer); pthread_create(&tid, NULL, thread2, NULL); pthread_join(tid, &pointer); printf("thread2 return value is %d\n", (int)pointer); pthread_create(&tid, NULL, thread3, NULL); pthread_cancel(tid); pthread_join(tid, &pointer); printf("thread3 return value is %d\n", (int)pointer); return 0; }
- 編譯
-
運行
-
thread增加取消點,線程退出
-
thread3不增加取消點(code_line25 //pthread_testcancel()),線程死鎖
-
-
5.設置線程爲detach遊離態,使用pthread_join判斷線程退出狀態
#include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread(void *arg) { printf("I am thread, my tid is %lu\n", pthread_self()); pthread_exit((void *)111); } int main(int args, char *argv[]) { pthread_t tid = 0; int error_flag = 0; void *status_pointer = NULL; pthread_create(&tid, NULL, thread, NULL); pthread_detach(tid); error_flag = pthread_join(tid, &status_pointer); printf("thread value is %d\n", (int)status_pointer); printf("error_flag is %d\n", error_flag); return 0; }
- 編譯
- 運行
-
line21將線程分離,可見detach後,線程退出後自動回收,其狀態無法再用join獲得
-
line21註釋,保持線程combination,正常獲取線程狀態111
-