pthread筆記
POSIX 表示可移植操作系統接口(Portable Operating System Interface,POSIX)。電氣和電子工程師協會(Institute of Electrical and Electronics Engineers,IEEE)最初開發 POSIX 標準,是為了提高 UNIX 環境下應用程序的可移植性。
- 具體的說 POSIX 是 IEEE 為要在各種 UNIX 操作系統上運行的軟件定義 API 所規定的一系列互相關聯的標準的總稱,而 X 則表明其對 Unix API 的傳承。
- Linux 基本上逐步實現了 POSIX 兼容,但並沒有參加正式的 POSIX 認證。
- 當前的 POSIX 文檔分為三個部分:POSIX Kernel API,POSIX 命令和工具集,及 POSIX 一致性測試。
- Posix 線程(POSIX threads,又稱 Pthreads)是負責 POSIX 的 IEEE 委員會開發的一套線程接口。
Linux 最初用的線程模型是 LinuxThread, 它不兼容 POSIX,而且存在一些性能問題,所以目前 Linux 摒棄了它,採用了基於 Pthreads 的 NPTL(Native POSIX Threads Library for Linux)模型, NPTL 修復了 LinuxThread 的許多缺點,並提供了更好的性能。
相對進程而言,執行緒是一個更加接近於執行體的概念,它可以與同進程中的其他執行緒共用資料,但擁有自己的棧空間,擁有獨立的執行序列。在串列程式基礎上引入執行緒和進程是為了提高程式的併發度,從而提高程式運行效率和回應時間。
執行緒和進程在使用上各有優缺點:
- 執行緒執行開銷小,但不利於資源的管理和保護;
- 而進程正相反。同時,執行緒適合於在SMP機器上運行,而進程則可以跨機器遷移。
資料類型
Pthreads API中大致共有100個函數調用,全都以"pthread_"開頭,並可以分為四類:
- 執行緒管理,例如創建執行緒,等待(join)執行緒,查詢執行緒狀態等。
- Mutex:創建、摧毀、鎖定、解鎖、設置屬性等操作
- 條件變量(Condition Variable):創建、摧毀、等待、通知、設置與查詢屬性等操作
- 使用了讀寫鎖的執行緒間的同步管理
pthread_t:執行緒句柄.出於移植目的,不能把它作為整數處理,應使用函數pthread_equal()對兩個執行緒ID進行比較。獲取自身所在執行緒id使用函數pthread_self()。
pthread_attr_t:執行緒屬性。主要包括scope屬性、detach屬性、堆疊地址、堆疊大小、優先級。主要屬性的意義如下:
- __detachstate,表示新執行緒是否與進程中其他執行緒脫離同步。如果設置為PTHREAD_CREATE_DETACHED,則新執行緒不能用pthread_join()來同步,且在退出時自行釋放所占用的資源。預設為PTHREAD_CREATE_JOINABLE狀態。一旦設置為PTHREAD_CREATE_DETACHED狀態,不論是創建時設置還是運行時設置,則不能再恢復到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。系統支持的最大和最小的優先級值可以用函數sched_get_priority_max和sched_get_priority_min得到。
- 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一值。
- __detachstate,表示新執行緒是否與進程中其他執行緒脫離同步。如果設置為PTHREAD_CREATE_DETACHED,則新執行緒不能用pthread_join()來同步,且在退出時自行釋放所占用的資源。預設為PTHREAD_CREATE_JOINABLE狀態。一旦設置為PTHREAD_CREATE_DETACHED狀態,不論是創建時設置還是運行時設置,則不能再恢復到PTHREAD_CREATE_JOINABLE狀態。
pthread_barrier_t:同步屏障數據類型
- pthread_mutex_t:mutex數據類型
- pthread_cond_t:條件變量數據類型
常用函式
- 執行緒操縱函數(簡介起見,省略參數):
- pthread_create():創建一個執行緒
- pthread_exit():終止當前執行緒
- pthread_cancel():請求中斷另外一個執行緒的運行。被請求中斷的執行緒會繼續運行,直至到達某個取消點(Cancellation Point)。取消點是執行緒檢查是否被取消並按照請求進行動作的一個位置。POSIX 的取消類型(Cancellation Type)有兩種,一種是延遲取消(PTHREAD_CANCEL_DEFERRED),這是系統默認的取消類型,即在執行緒到達取消點之前,不會出現真正的取消;另外一種是異步取消(PHREAD_CANCEL_ASYNCHRONOUS),使用異步取消時,執行緒可以在任意時間取消。系統調用的取消點實際上是函數中取消類型被修改為異步取消至修改回延遲取消的時間段。幾乎可以使執行緒掛起的庫函數都會響應CANCEL信號,終止執行緒,包括sleep、delay等延時函數。
- pthread_join():阻塞當前的執行緒,直到另外一個執行緒運行結束
- pthread_kill():向指定ID的執行緒發送一個信號,如果執行緒不處理該信號,則按照信號默認的行為作用於整個進程。信號值0為保留信號,作用是根據函數的返回值判斷執行緒是不是還活著。
- pthread_cleanup_push():執行緒可以安排異常退出時需要調用的函數,這樣的函數稱為執行緒清理程序,執行緒可以建立多個清理程序。執行緒清理程序的入口地址使用棧保存,實行先進後處理原則。由pthread_cancel或pthread_exit引起的執行緒結束,會次序執行由pthread_cleanup_push壓入的函數。執行緒函數執行return語句返回不會引起執行緒清理程序被執行。
- pthread_cleanup_pop():以非0參數調用時,引起當前被彈出的執行緒清理程序執行。
- pthread_setcancelstate():允許或禁止取消另外一個執行緒的運行。
- pthread_setcanceltype():設置執行緒的取消類型為延遲取消或異步取消。
*執行緒屬性函數:
+ pthread_attr_init():初始化執行緒屬性變量。運行後,pthread_attr_t結構所包含的內容是作業系統支持的執行緒的所有屬性的默認值。
+ pthread_attr_setdetachstate():設置執行緒屬性變量的detachstate屬性(決定執行緒在終止時是否可以被 joinable)
+ pthread_attr_getdetachstate():獲取脫離狀態的屬性
+ pthread_attr_setscope():設置執行緒屬性變量的__scope屬性
+ pthread_attr_setschedparam():設置執行緒屬性變量的schedparam屬性,即調用的優先級。
+ pthread_attr_getschedparam():獲取執行緒屬性變量的schedparam屬性,即調用的優先級。
+ pthread_attr_destroy():刪除執行緒的屬性,用無效值覆蓋
mutex函數:
- pthread_mutex_init() 初始化互斥鎖
- pthread_mutex_destroy() 刪除互斥鎖
- pthread_mutex_lock():占有互斥鎖(阻塞操作)
- pthread_mutex_trylock():試圖占有互斥鎖(不阻塞操作)。即,當互斥鎖空閒時,將占有該鎖;否則,立即返回。
- pthread_mutex_unlock(): 釋放互斥鎖
- pthreadmutexattr(): 互斥鎖屬性相關的函數
條件變量函數:
- pthread_cond_init():初始化條件變量
- pthread_cond_destroy():銷毀條件變量
- pthread_cond_signal(): 發送一個信號給正在當前條件變量的執行緒隊列中處於阻塞等待狀態的執行緒,使其脫離阻塞狀態,喚醒後繼續執行。如果沒有執行緒處在阻塞等待狀態,pthread_cond_signal也會成功返回。一般只給一個阻塞狀態的執行緒發信號。假如有多個執行緒正在阻塞等待當前條件變量,則根據各等待執行緒優先級的高低確定哪個執行緒接收到信號開始繼續執行。如果各執行緒優先級相同,則根據等待時間的長短來確定哪個執行緒獲得信號。但pthread_cond_signal在多處理器上可能同時喚醒多個執行緒,當只能讓一個被喚醒的執行緒處理某個任務時,其它被喚醒的執行緒就需要繼續wait。POSIX規範要求pthread_cond_signal至少喚醒一個pthread_cond_wait上的執行緒,有些實現為了簡便,在單處理器上也會喚醒多個執行緒。所以最好對pthread_cond_wait()使用while循環對條件變量是否滿足做條件判斷。
pthread_cond_wait(): 等待條件變量的特殊條件發生;pthread_cond_wait() 必須與一個pthread_mutex配套使用。該函數調用實際上依次做了3件事:對當前pthread_mutex解鎖、把當前執行緒掛起到當前條件變量的執行緒隊列、被其它執行緒的信號喚醒後對當前pthread_mutex申請加鎖。如果執行緒收到一個信號被喚醒,將被配套的互斥鎖重新鎖住,pthread_cond_wait() 函數將不返回直到執行緒獲得配套的互斥鎖。需要注意的是,一個條件變量不應該與多個互斥鎖配套使用。
pthread_cond_broadcast(): 某些應用,如執行緒池,pthread_cond_broadcast喚醒全部執行緒,但我們通常只需要一部分執行緒去做執行任務,所以其它的執行緒需要繼續wait.
- pthreadcondattr(): 條件變量屬性相關的函數
執行緒私有存儲(Thread-local storage): +pthread_key_create(): 分配用於標識進程中執行緒特定數據的pthread_key_t類型的鍵
- pthread_key_delete(): 銷毀現有執行緒特定數據鍵
- pthread_setspecific(): 為指定執行緒的特定數據鍵設置綁定的值
- pthread_getspecific(): 獲取調用執行緒的鍵綁定值,並將該綁定存儲在 value 指向的位置中
同步屏障函數
- pthread_barrier_init(): 同步屏障初始化
- pthread_barrier_wait():
- pthread_barrier_destory():
其它多執行緒同步函數:
- pthreadrwlock*(): 讀寫鎖
工具函數:
- pthread_equal(): 對兩個執行緒的執行緒標識號進行比較
- pthread_detach(): 分離執行緒
- pthread_self(): 查詢執行緒自身執行緒標識號
- pthread_once(): 某些需要僅執行一次的函數。其中第一個參數為pthread_once_t類型,是內部實現的互斥鎖,保證在程序全局僅執行一次。
信號量函數,包含在semaphore.h中:
- sem_open:創建或者打開已有的命名信號量。可分為二值信號量與計數信號量。命名信號量可以在進程間共享使用。
- sem_close:關閉一個信號燈,但沒有將它從系統中刪除。命名信號燈是隨內核持續的,即使當前沒有進程打開著某個信號燈,它的值仍然保持。
- sem_unlink:從系統中刪除信號燈。
- sem_getvalue:返回所指定信號燈的當前值。如果該信號燈當前已上鎖,那麼返回值或為0,或為某個負數,其絕對值就是等待該信號燈解鎖的執行緒數。
- sem_wait:申請共享資源,所指定信號燈的值如果大於0,那就將它減1並立即返回,就可以使用申請來的共享資源了。如果該值等於0,調用執行緒就被進入睡眠狀態,直到該值變為大於0,這時再將它減1,函數隨後返回。sem_wait操作必須是原子操作。
- sem_trywait:申請共享資源,當所指定信號燈的值已經是0時,後者並不將調用執行緒投入睡眠。相反,它返回一個EAGAIN錯誤。
- sem_post:釋放共享資源。與sem_wait恰相反。
- sem_init:初始化非命名(內存)信號量
- sem_destroy:摧毀非命名信號量
共享內存函數,包含在sys/mman.h中,連結時使用rt庫:
- mmap:把一個文件或一個POSIX共享內存區對象映射到調用進程的地址空間。使用該函數的目的: 1.使用普通文件以提供內存映射I/O 2.使用特殊文件以提供匿名內存映射。 3.使用shm_open以提供無親緣關係進程間的Posix共享內存區。
- munmap: 刪除一個映射關係
- msync:文件與內存同步函數
- shm_open:創建或打開共享內存區
- shm_unlink:刪除一個共享內存區對象的名字,刪除一個名字僅僅防止後續的open,msq_open或sem_open調用取得成功。
- ftruncate:調整文件或共享內存區大小
- fstat來獲取有關該對象的信息
編譯方法
Compiler | command | language |
---|---|---|
GNU C compiler | gcc -pthread | c |
GNU CPP compiler | g++ -pthread | c++ |
Intel C compiler | icc -pthread | c |
Intel CPP compiler | icpc -pthread | c++ |
基本範例
- 編譯指令 gcc -o thread -pthread thread.c
// thread.c
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 5
void *PrintHello(void *threadid) {
/* 被thread執行的函數 */
long tid;
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
/* thread執行結束以pthread_exit離開 */
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int rc;
long t;
for(t=0; t<NUM_THREADS; t++){
printf("In main: creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
/* Last thing that main() should do */
pthread_exit(NULL);
return 0;
}
創建線程
int pthread_create(pthread_t* tid, pthread_attr_t * attr,
void * (*start_routine)(void *), void* arg)
- POSIX通過pthread_create()函數創建執行緒。
- 與fork()調用創建一個進程的方法不同,pthread_create()創建的執行緒並不具備與主執行緒(即調用pthread_create()的執行緒)同樣的執行序列,而是使其運行start_routine(arg)函數。
- 參數1. pthread_t *tid為pthread的指標,在使用Thread之前必須要先宣告一個pthread_t的變數。
- 參數2. const pthread_attr_t *attr為該Thread的屬性,預設是NULL,如果沒有其他特殊的需求直接填入NULL即可。
- 參數3. void *(*start_routine)(void *)為Function pointer,這邊要放入你要執行的Function。
- 參數4. void *argument為Function pointer所要帶的參數。
- 回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
void pthread_exit (void *value_ptr)
- 此Function的作用是用來關閉一個Thread,附帶有1個參數。
- 參數1: void *value_ptr用來設定執行成功時該Thread會回傳的值,這個值可由pthread_join()這個Function來取得。
- 回傳值: 不會回傳任何值。
int pthread_cancel (pthread_t thread)
- 這個Function的作用是用來關閉一個指定的Thread,附帶有一個參數。
- 參數1: pthread_t thread為要關閉的Thread。
- 回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
int pthread_join (pthread_t thread, void **value_ptr)
```*
* 這個Function的作用會暫停目前執行pthread_join的Thread,等到目標Thread執行完畢之後目前執行pthread_join的Thread才會繼續執行,附帶有2個參數。
+ 參數1: pthread_t thread為要等待的目標Thread。
+ 參數2: void \*\*value_ptr用來取得目標Thread的回傳值。
+ 回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
```c
int pthread_kill (pthread_t thread , int sig)
- 這個Function的作用為傳送訊息給目標的Thread。
- 參數1: pthread_t thread為訊息要傳送到的目的地Thread。
- 參數2: int sig為要傳送的訊息。(當sig為0時為保留訊號,用來測試目標Thread是否還存活)
- 回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。(如果pthread_kill執行失敗則不會傳送任何的訊息(sig))該Thread不存在則回傳ESRCH,sig不合法則回傳EINVAL。
#include <stdio.h>
#include <pthread.h>
void *show_message( void *ptr )
{
char *message;
message = (char *) ptr;
int x = 5;
for(x = 5 ; x > 0 ; --x){
printf("%s \n", message);
_sleep(300);
}
pthread_exit((void *)1234);
}
int main(){
pthread_t thread1;
char *message1 = "Thread 1";
void *ret;
pthread_create(&thread1, NULL , show_message , (void*) message1);
pthread_join( thread1, &ret);
printf("return value from thread1 = %d\n",ret);
system("pause");
return 0;
}
// results
Thread 1
Thread 1
Thread 1
Thread 1
Thread 1
return value from thread1 = 1234
參考文章
POSIX線程(pthread)入門文章分享%E5%85%A5%E9%96%80%E6%96%87%E7%AB%A0%E5%88%86%E4%BA%AB)