1. 程式人生 > >7.9-UC-第九課:線程管理

7.9-UC-第九課:線程管理

編程 回調 管理 函數棧 特性 分配 -- get 高效

================第九課 線程管理================
一、基本概念------------
1. 線程就是程序的執行路線,即進程內部的控制序列, 或者說是進程的子任務。
2. 線程,輕量級,不擁有自己獨立的內存資源, 共享進程的代碼區、數據區、堆區(註意沒有棧區)、 環境變量和命令行參數、文件描述符、信號處理函數、 當前目錄、用戶ID和組ID等資源。
3. 線程擁有自己獨立的棧,因此也有自己獨立的局部變量。
4. 一個進程可以同時擁有多個線程, 即同時被系統調度的多條執行路線, 但至少要有一個主線程。
二、基本特點------------
1. 線程是進程的一個實體, 可作為系統獨立調度和分派的基本單位。
2. 線程有不同的狀態,系統提供了多種線程控制原語, 如創建線程、銷毀線程等等。
3. 線程不擁有自己的資源,只擁有從屬於進程的全部資源, 所有的資源分配都是面向進程的。
4. 一個進程中可以有多個線程並發地運行。 它們可以執行相同的代碼,也可以執行不同的代碼。
5. 同一個進程的多個線程都在同一個地址空間內活動, 因此相對於進程,線程的系統開銷小,任務切換快。
6. 線程間的數據交換不需要依賴於類似IPC的特殊通信機制, 簡單而高效。
7. 每個線程擁有自己獨立的線程ID、寄存器信息、函數棧、 錯誤碼和信號掩碼。
8. 線程之間存在優先級的差異。
三、POSIX線程(pthread)----------------------
1. 早期廠商各自提供私有的線程庫版本, 接口和實現的差異非常大,不易於移植。
2. IEEE POSIX 1003.1c (1995)標準, 定義了統一的線程編程接口, 遵循該標準的線程實現被統稱為POSIX線程,即pthread。
3. pthread包含一個頭文件pthread.h, 和一個接口庫libpthread.so。
#include <pthread.h>
...
gcc ... -lpthread
4. 功能
1) 線程管理:創建/銷毀線程、分離/聯合線程、 設置/查詢線程屬性。
2) 線程同步
A. 互斥量:創建/銷毀互斥量、加鎖/解鎖互斥量、 設置/查詢互斥量屬性。
B. 條件變量:創建/銷毀條件變量、等待/觸發條件變量、 設置/查詢條件變量屬性。
四、線程函數------------
1. 創建線程~~~~~~~~~~~
int pthread_create (pthread_t* restrict thread, const pthread_attr_t* restrict attr, void* (*start_routine) (void*), void* restrict arg);
thread - 線程ID,輸出參數。 pthread_t即unsigned long int。
attr - 線程屬性,NULL表示缺省屬性。 pthread_attr_t可能是整型也可能是結構, 因實現而異。
start_routine - 線程過程函數指針, 參數和返回值的類型都是void*。 啟動線程本質上就是調用一個函數, 只不過是在一個獨立的線程中調用的, 函數返回即線程結束。
arg - 傳遞給線程過程函數的參數。 線程過程函數的調用者是系統內核, 而非用戶代碼, 因此需要在創建線程時指定參數。
成功返回0,失敗返回錯誤碼。
註意:
1) restrict: C99引入的編譯優化指示符, 提高重復解引用同一個指針的效率。
2) 在pthread.h頭文件中聲明的函數, 通常以直接返回錯誤碼的方式表示失敗, 而非以錯誤碼設置errno並返回-1。
3) main函數即主線程,main函數返回即主線程結束, 主線程結束即進程結束, 進程一但結束其所有的線程即結束。
4) 應設法保證在線程過程函數執行期間, 其參數所指向的目標持久有效。
創建線程。範例:create.c
線程並發。範例:concur.c
線程參數。範例:arg.c
2. 等待線程~~~~~~~~~~~
int pthread_join (pthread_t thread, void** retval);
等待thread參數所標識的線程結束,成功返回0,失敗返回錯誤碼。
範例:ret.c
註意從線程過程函數中返回值的方法:
1) 線程過程函數將所需返回的內容放在一塊內存中, 返回該內存的地址,保證這塊內存在函數返回, 即線程結束,以後依然有效;
2) 若retval參數非NULL, 則pthread_join函數將線程過程函數所返回的指針, 拷貝到該參數所指向的內存中;
3) 若線程過程函數所返回的指針指向動態分配的內存, 則還需保證在用過該內存之後釋放之。
3. 獲取線程自身的ID~~~~~~~~~~~~~~~~~~~
pthread_t pthread_self (void);
成功返回調用線程的ID,不會失敗。
4. 比較兩個線程的ID~~~~~~~~~~~~~~~~~~~
int pthread_equal (pthread_t t1, pthread_t t2);
若參數t1和t2所標識的線程ID相等,則返回非零,否則返回0。
某些實現的pthread_t不是unsigned long int類型,可能是結構體類型,無法通過“==”判斷其相等性。
範例:equal.c
5. 終止線程~~~~~~~~~~~
1) 從線程過程函數中return。
2) 調用pthread_exit函數。
void pthread_exit (void* retval);
retval - 和線程過程函數的返回值語義相同。
註意:在任何線程中調用exit函數都將終止整個進程。
範例:exit.c
6. 線程執行軌跡~~~~~~~~~~~~~~~
1) 同步方式(非分離狀態): 創建線程之後調用pthread_join函數等待其終止, 並釋放線程資源。
2) 異步方式(分離狀態): 無需創建者等待,線程終止後自行釋放資源。
int pthread_detach (pthread_t thread);
使thread參數所標識的線程進入分離(DETACHED)狀態。處於分離狀態的線程終止後自動釋放線程資源,且不能被pthread_join函數等待。
成功返回0,失敗返回錯誤碼。
範例:detach.c
7. 取消線程~~~~~~~~~~~
1) 向指定線程發送取消請求
int pthread_cancel (pthread_t thread);
成功返回0,失敗返回錯誤碼。
註意:該函數只是向線程發出取消請求,並不等待線程終止。
缺省情況下,線程在收到取消請求以後,並不會立即終止,而是仍繼續運行,直到其達到某個取消點。在取消點處,線程檢查其自身是否已被取消了,並做出相應動作。
當線程調用一些特定函數時,取消點會出現。
2) 設置調用線程的可取消狀態
int pthread_setcancelstate (int state, int* oldstate);
成功返回0,並通過oldstate參數輸出原可取消狀態(若非NULL),失敗返回錯誤碼。
state取值:
PTHREAD_CANCEL_ENABLE - 接受取消請求(缺省)。
PTHREAD_CANCEL_DISABLE - 忽略取消請求。
3) 設置調用線程的可取消類型
int pthread_setcanceltype (int type, int* oldtype);
成功返回0,並通過oldtype參數輸出原可取消類型(若非NULL),失敗返回錯誤碼。
type取值:
PTHREAD_CANCEL_DEFERRED - 延遲取消(缺省)。
被取消線程在接收到取消請求之後並不立即響應,而是一直等到執行了特定的函數(取消點)之後再響應該請求。
PTHREAD_CANCEL_ASYNCHRONOUS - 異步取消。
被取消線程可以在任意時間取消,不是非得遇到取消點才能被取消。但是操作系統並不能保證這一點。
範例:cancel.c
8. 線程屬性~~~~~~~~~~~
創建線程函數int pthread_create (pthread_t* restrict thread, const pthread_attr_t* restrict attr, void* (*start_routine) (void*), void* restrict arg);的第二個參數即為線程屬性,傳空指針表示使用缺省屬性。
typedef struct { // 分離狀態 // // PTHREAD_CREATE_DETACHED // - 分離線程。 // // PTHREAD_CREATE_JOINABLE(缺省) // - 可匯合線程。 // int detachstate;
// 競爭範圍 // // PTHREAD_SCOPE_SYSTEM // - 在系統範圍內競爭資源。 // // PTHREAD_SCOPE_PROCESS(Linux不支持) // - 在進程範圍內競爭資源。 // int scope;
// 繼承特性 // // PTHREAD_INHERIT_SCHED(缺省) // - 調度屬性自創建者線程繼承。 // // PTHREAD_EXPLICIT_SCHED // - 調度屬性由後面兩個成員確定。 // int inheritsched;
// 調度策略 // // SCHED_FIFO // - 先進先出策略。 // // 沒有時間片。 // // 一個FIFO線程會持續運行, // 直到阻塞或有高優先級線程就緒。 // // 當FIFO線程阻塞時,系統將其移出就緒隊列, // 待其恢復時再加到同優先級就緒隊列的末尾。 // // 當FIFO線程被高優先級線程搶占時, // 它在就緒隊列中的位置不變。 // 因此一旦高優先級線程終止或阻塞, // 被搶占的FIFO線程將會立即繼續運行。 // // SCHED_RR // - 輪轉策略。 // // 給每個RR線程分配一個時間片, // 一但RR線程的時間片耗盡, // 系統即將移到就緒隊列的末尾。 // // SCHED_OTHER(缺省) // - 普通策略。 // // 靜態優先級為0。任何就緒的FIFO線程或RR線程, // 都會搶占此類線程。 // int schedpolicy;
// 調度參數 // // struct sched_param { // int sched_priority; /* 靜態優先級 */ // }; // struct sched_param schedparam;
// 棧尾警戒區大小(字節) // // 缺省一頁(4096字節)。 // size_t guardsize;
// 棧地址 // void* stackaddr;
// 棧大小(字節) // size_t stacksize;} pthread_attr_t;
不要手工讀寫該結構體,而應調用pthread_attr_set/get函數設置/獲取具體屬性項。
1) 設置線程屬性
第一步,初始化線程屬性結構體
int pthread_attr_init (pthread_attr_t* attr);
第二步,設置具體線程屬性項
int pthread_attr_setdetachstate ( pthread_attr_t* attr, int detachstate);
int pthread_attr_setscope ( pthread_attr_t* attr, int scope);
int pthread_attr_setinheritsched ( pthread_attr_t* attr, int inheritsched);
int pthread_attr_setschedpolicy ( pthread_attr_t* attr, int policy);
int pthread_attr_setschedparam ( pthread_attr_t* attr, const struct sched_param* param);
int pthread_attr_setguardsize ( pthread_attr_t* attr, size_t guardsize);
int pthread_attr_setstackaddr ( pthread_attr_t* attr, void* stackaddr);
int pthread_attr_setstacksize ( pthread_attr_t* attr, size_t stacksize);
int pthread_attr_setstack ( pthread_attr_t* attr, void* stackaddr, size_t stacksize);
第三步,以設置好的線程屬性結構體為參數創建線程
int pthread_create (pthread_t* restrict thread, const pthread_attr_t* testrict attr, void* (*start_routine) (void*), void* restrict arg);
第四步,銷毀線程屬性結構體
int pthread_attr_destroy (pthread_attr_t* attr);
2) 獲取線程屬性
第一步,獲取線程屬性結構體
int pthread_getattr_np (pthread_t thread, pthread_attr_t* attr);
第二步,獲取具體線程屬性項
int pthread_attr_getdetachstate ( pthread_attr_t* attr, int* detachstate);
int pthread_attr_getscope ( pthread_attr_t* attr, int* scope);
int pthread_attr_getinheritsched ( pthread_attr_t* attr, int* inheritsched);
int pthread_attr_getschedpolicy ( pthread_attr_t* attr, int* policy);
int pthread_attr_getschedparam ( pthread_attr_t* attr, struct sched_param* param);
int pthread_attr_getguardsize ( pthread_attr_t* attr, size_t* guardsize);
int pthread_attr_getstackaddr ( pthread_attr_t* attr, void** stackaddr);
int pthread_attr_getstacksize ( pthread_attr_t* attr, size_t* stacksize);
int pthread_attr_getstack ( pthread_attr_t* attr, void** stackaddr, size_t* stacksize);
以上所有函數成功返回0,失敗返回錯誤碼。
範例:attr.c

來自為知筆記(Wiz)

7.9-UC-第九課:線程管理