注:筆者這些文檔多出自APUE這本書,旨在督促自己堅持學習,其中沒有給出實際的編碼例子,是因爲本身快速學習的原則,即“花20%的時間掌握80%的內容”,具體的實戰會在後續網絡編程中一一體現。個人覺得學習編程最佳的路徑是在解決問題中掌握知識點。因此線程這幾篇都只會講述個人理解的基礎概念。
線程創建的相關函數:
1. int pthread_create(pthread_t*thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);
返回值:若成功,返回0;否則返回錯誤編號
Eg.
pthread_t thread_id;
pthread_create(thread_id, &attr,&thread_start, &tinfo[tnum]);
- thread_id---------線程創建成功後用於存放線程ID的位置
- attr--------------線程的屬性,若設置爲NULL則表示默認
- thread_start------新創建的線程從thread_start函數的地址開始運行
- tinfo[tnum]-------需要向thread_start函數傳遞的參數
返回值:對比t1和t2兩個線程是否相等,若相等返回非0數值;否則,返回0
- 線程ID只有在它所屬的進程上下文中才有意義,這點和進程ID是有區別的。
返回值:調用線程的線程ID
線程終止:
線程的終止分爲兩大類:主動終止,被動終止
主動終止
- 線程函數執行return正常返回,返回值就是線程的退出碼;
- 線程調用pthread_exit函數,其參數就是線程的退出碼;
被動終止
- 在其他線程中調用pthread_cancel函數;
- 任意線程調用exit家族的函數,這種方式比較極端,會導致整個進程退出;
1. void pthread_exit(void*retval);
Eg.
pthread_exit((void *)2);
- retval-----------線程的返回值,即終止狀態
返回值:若成功,返回0,;否則,返回錯誤編號
- 線程通過調用這個函數來請求(並不等待線程終止,它僅僅是提出請求)取消同一進程中的其他線程。
它的行爲等同於pthread_exit(PTHREAD_CANCELED);
3. int pthread_join(pthread_tthread, void **retval);
返回值:若成功,返回0;否則,返回錯誤編號
調用線程將一直阻塞,直到指定的線程調用pthread_exit,從啓動例程中返回或者被取消。
retval------如果線程從啓動例程中返回,則retval就包含返回碼
retval------如果線程被取消,就由retval指定的單元被設置爲PTHREAD_CANCELED
- 在調用pthread_join的時候還需要直到線程的啓動狀態,如果線程是分離狀態啓動的那麼,pthread_join調用就會失敗,返回EINVAL,如果是以非分離狀態啓動的pthread_join就會把線程置於分離狀態,這樣就可以回收線程資源。
int pthread_detach(pthread_tthread);
返回值:若成功,返回0;否則,返回錯誤編號。
線程清理處理程序:
線程可以安排它退出時需要調用的函數,這與進程在退出時可以使用atexit函數註冊退出函數類似。一個線程可以建立多個清理處理程序。處理程序記錄在棧中,也就是說,他們執行順序與他們註冊時相反。
1. void pthread_cleanup_push(void (*rtn)(void *), void *arg);rtn------清理函數rtn是由pthread_cleanup_push函數調度的,調用時只有一個參數arg;
2. voidpthread_cleanup_pop(intexecute);
execute------參數設置爲0時,清理函數不被調用。
無論如何pthread_cleanup_pop都將會刪除上次pthread_cleanup_push調用建立的清理處理程序。
注意:pthread_cleanup_push和pthread_cleanup_pop必須成對使用。這個可以做個試驗測試一下,註釋掉你的pthread_cleanup_pop編譯會產生什麼結果。
線程和進程之間的相似之處:
進程原語 |
線程原語 |
描述 |
fork |
pthread_create |
創建新的控制流 |
exit |
pthread_exit |
從現有的控制流中退出 |
waitpid |
pthread_join |
從控制流中得到退出狀態 |
atexit |
pthread_cleanup_push |
註冊退出清理函數 |
getpid |
pthread_self |
獲取控制流ID |
abort |
pthread_cancel |
請求控制流的非正常退出 |
線程屬性:
在前面創建線程的時候我們調用pthread_create函數中有個參數,
const pthread_attr_t *attr是關於設置線程屬性的。
接下來就詳細總結一下Linux線程的屬性。
pthread_attr_t 這個結構體表示將爲創建的線程指定類型,該類型的結構體包含了如下屬性:
1. detachstate:線程的分離屬性
它表示線程結束的時候,是否回收資源。
- PTHREAD_CREATE_JOINABLE:這種是默認屬性,表示在線程結束時不回收資源,需要調用pthread_join函數來回收;
- PTHREAD_CREATE_DETACHED:表示線程結束,直接釋放資源;
用於設置和獲取detach state的函數如下:
int pthread_attr_setdetachstate(pthread_attr_t *attr,int *detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t*restrict attr, int *detachstet );
2. scope:線程的競爭域
該屬性有兩種:
- PTHREAD_SCOPE_SYSTEM:表示線程和整個操作系統中的線程進行競爭;
- PTHREAD_SCOPE_PROCESS:表示線程只和當前進程內的線程進行競爭,比如線程調度的時候只考慮當前進程中的線程;
用來設置和獲取scope的函數如下:
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t*attr, int *scope);
3. inheritscheduler:調度繼承性
該屬性有兩種:
- PTHREAD_INHERIT_SCHED:繼承進程的調度策略
- PHTREAD_EXPLICIT_SCHED:不使用繼承的調度策略,而是使用自己提供的調度策略。
用來設置和獲取inherit scheduler的函數如下:
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_ getinheritsched(const pthread_attr_t*attr, int *inheritsched);
4. schedulingpolicy:調度策略
分兩大類:
- 普通調度策略(非實時的調度策略):主要包括SCHED_OTHER、SCHED_BATCH和SCHED_IDLE
- 實時調度策略:主要包括SCHED_FIFO和SCHED_RR
用於設置和獲取policy的函數如下:
int pthread_attr_setschedpoilcy (pthread_attr_t *attr, int poilcy);
int pthread_attr_ getschedpoilcy(const pthread_attr_t*attr, int *poilcy);
5. schedulingpriority:線程優先級
使用如下函數來設置和獲取:
pthread_attr_setschedparm(pthread_attr_t *attr,const structsched_param *param);
pthread_attr_getschedparm(pthread_attr_t *attr, struct sched_param *param);
6. guardsize:警界緩衝區大小,默認值是PAGE_SIZE(4096KB)
用來設置和獲取線程棧末尾的警戒緩衝區大小,如果線程棧運行到了警戒區,就會收到信號。
使用如下函數來設置和獲取:
int pthread_attr_getguardsize(constpthread_attr_t *attr, size_t *guardsize);
int pthread_attr_setguardsize(pthread_attr_t*attr, size_t guardsize);
7. stackaddress:棧起始地址(棧最低內存地址)
一般來說在X86或者X64處理器上,棧都是往地地址方向的,所以該值一般表示棧的末尾(棧頂)
使用如下兩個函數來對其進行設置和獲取:
int pthread_attr_getstack(constpthread_attr_t *attr, void **stackaddr, size_t *stacksize);
int pthread_attr_setstack(pthread_attr_t*attr, void *stackaddr, size_t stacksize);
8. stacksize:棧大小
對於棧的大小,可以使用如下函數進行設置和獲取:
intpthread_attr_getstacksize(constpthread_attr_t *attr, size_t*stacksize);
intpthread_attr_setstacksize(pthread_attr_t*attr, size_tstacksize);
以上屬性目前我最常用的就是detach state(線程的分離屬性)。