Posix線程 它們那一大家子事兒,要覺得好你就收藏進被窩里慢慢看 (1)
文章目錄
①大神博客先推
②好,現在看我的
線程是啥玩意兒? 非要線程不可?
線程與進程千絲萬縷的糾纏
線程間資源共享情況
⑴共享資源
⑵非共享資源
線程的缺點
③線程安全問題
這個模塊他的博客里沒
④嗶嗶完了不?放碼過來!
創建線程
接下來演示線程安全:
獲取當前線程id
判斷倆線程是否相等
單次初始化
連接(Joining)和分離(Detaching)線程
又到了演示線程安全的時間了
⑤線程屬性
⑥敲黑板:棧管理
準備好小板凳
未完待續···
①大神博客先推
我這人就是這么的無私,這位大神應該是退隱好幾年了,但是留下了不少好東西。
Posix線程詳解
不過我的小白文也有不少彩蛋哦↓↓↓
②好,現在看我的
畢竟大神的博客也太長了,而且深奧,大家就好了。
咱這個短,通俗易懂。
線程是啥玩意兒? 非要線程不可?
官方話就是:是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。
1、提高程序的并發性 2、開銷小,不需要重新分配內存 3、通信和共享數據方便
1
2
3
還真別說,大神就是厲害,這還有圖有真相!!!
線程與進程千絲萬縷的糾纏
(1)線程又被叫做輕量級進程,也有PCB,創建線程使用的底層函數和進程是一樣的,都是clone。 (2)從內核里看線程和進程是一樣的,都有各自不同的PCB,但是PCB指向的內存資源的三級頁表是不同的。 (3)進程可以蛻變成線程,進程也可以說是主線程,就是高速路的主干道。 (4)在Linux下,線程是最小的執行單位,進程是最小的分配資源單位。
1
2
3
4
線程間資源共享情況
1、文件描述符表
2、每種信號的處理方式
3、當前工作目錄
4、用戶ID和組ID
5、內存地址空間
1、線程id
2、處理器現場和棧指針
3、獨立的棧空間
4、errno變量
5、信號屏蔽字
6、調度優先級
線程的缺點
1、線程不穩定(這個是真的不穩定,后面章節會提) 2、線程調試困難(這個是真的頭疼,難以調試的東西) 3、線程無法使用Unix經典事件,如信號
1
2
3
③線程安全問題
線程安全(Thread-safeness):
線程安全:簡短的說,指程序可以同時執行多個線程卻不會“破壞“共享數據或者產生“競爭”條件的能力。
例如:假設你的程序創建了幾個線程,每一個調用相同的庫函數:
這個庫函數存取/修改了一個全局結構或內存中的位置。
當每個線程調用這個函數時,可能同時去修改這個全局結構活內存位置。
如果函數沒有使用同步機制去阻止數據破壞,這時,就不是線程安全的了。
這個模塊他的博客里沒
嘿嘿,如果看我的博客,那這就是一個彩蛋了。
我有???可重入函數對于線程安全的意義(附函數表)
④嗶嗶完了不?放碼過來!
創建線程
pthread_create
1
功能:創建一個線程
原語函數:
#include
1
2
3
參數釋義:
thread:傳遞一個pthread_t變量進來,用以保存新線程的tid(線程id)
attr:線程屬性設置,NULL代表使用默認屬性(注(1))
(*start_routine)(void *):函數指針,指向新線程應該指向的函數模塊
arg:老熟了,給前面那個函數傳參用的,不傳就寫NULL
返回值:成功返回0.,失敗返回錯誤號,錯誤號,錯誤號,前面說過errno不共享的。(線程里返回值統一這樣的,后面不提了)
注(1):創建線程時,沒什么特殊情況我們都是使用默認屬性的,不過有時候需要做一些特殊處理,碧如調整優先級啊這些的。后面會說。
Q:怎樣安全地向一個新創建的線程傳遞數據?
A:確保所傳遞的數據是線程安全的(不能被其他線程修改)。下面三個例子演示了那個應該和那個不應該。
我的代碼太菜了,看那個大神的:
Example Code - Pthread Creation and Termination #include 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 //下面的代碼片段演示了如何向一個線程傳遞一個簡單的整數。 //主線程為每一個線程使用一個唯一的數據結構,確保每個線程傳遞的參數是完整的。 int *taskids[NUM_THREADS]; for(t=0; t 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //例子展示了用結構體向線程設置/傳遞參數。每個線程獲得一個唯一的結構體實例。 struct thread_data{ int thread_id; int sum; char *message; }; struct thread_data thread_data_array[NUM_THREADS]; void *PrintHello(void *threadarg) { struct thread_data *my_data; ... my_data = (struct thread_data *) threadarg; taskid = my_data->thread_id; sum = my_data->sum; hello_msg = my_data->message; ... } int main (int argc, char *argv[]) { ... thread_data_array[t].thread_id = t; thread_data_array[t].sum = sum; thread_data_array[t].message = messages[t]; rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &thread_data_array[t]); ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 //例子演示了錯誤地傳遞參數。循環會在線程訪問傳遞的參數前改變傳遞給線程的地址的內容。 int rc, t; for(t=0; t 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 獲取當前線程id pthread_self 1 功能:獲取調用線程tid(注(3)) 注(3):有的人就要問了,這東西不是都傳出來了嗎,直接打印不就完事兒了嗎,為什么還要特地開一個函數去獲取? 是這樣的,線程id的類型是pthread_t,它在當前進程中是唯一的,但是在不同系統中這個類型有不同的實現,它可能是一個整數值,也可能是一個結構體,反正就是你猜不到的東西。 好,看原語: #include 1 2 3 判斷倆線程是否相等 pthread_equal 1 功能:判斷兩個線程是否相等 原語: #include 1 2 3 注意這兩個函數中的線程ID對象是不透明的,不是輕易能檢查的。因為線程ID是不透明的對象,所以C語言的==操作符不能用于比較兩個線程ID。 單次初始化 pthread_once (once_control, init_routine) pthread_once 在一個進程中僅執行一次init_routine。 任何線程第一次調用該函數會執行給定的init_routine,不帶參數,任何后續調用都沒有效果。 init_routine函數一般是初始化的程序 once_control參數是一個同步結構體,需要在調用pthread_once前初始化。 1 2 3 4 5 6 例如: pthread_once_t once_control = PTHREAD_ONCE_INIT; 1 連接(Joining)和分離(Detaching)線程 pthread_join(threadid,status) pthread_detach(threadid,status) pthread_attr_setdetachstate(attr,detachstate) pthread_attr_getdetachstate(attr,detachstate) 1 2 3 4 5 6 7 連接: “連接”是一種在線程間完成同步的方法。例如: > pthread_join()函數阻賽調用線程直到threadid所指定的線程終止。 > 如果在目標線程中調用pthread_exit(),程序員可以在主線程中獲得目標線程的終止狀態。 > > 連接線程只能用pthread_join()連接一次。若多次調用就會發生邏輯錯誤。 > > 兩種同步方法,互斥量(mutexes)和條件變量(condition variables),稍后討論。 1 2 3 4 5 6 可連接(Joinable or Not)? 當一個線程被創建,它有一個屬性定義了它是可連接的(joinable)還是分離的(detached)。 只有是可連接的線程才能被連接(joined),若果創建的線程是分離的,則不能連接。 POSIX標準的最終草案指定了線程必須創建成可連接的。然而,并非所有實現都遵循此約定。 使用pthread_create()的attr參數可以顯式的創建可連接或分離的線程 1 2 3 4 5 6 7 典型四步如下: 聲明一個pthread_attr_t數據類型的線程屬性變量 用 pthread_attr_init()初始化改屬性變量 用pthread_attr_setdetachstate()設置可分離狀態屬性 完了后,用pthread_attr_destroy()釋放屬性所占用的庫資源 1 2 3 4 5 6 7 分離(Detaching): pthread_detach()可以顯式用于分離線程,盡管創建時是可連接的。 沒有與pthread_detach()功能相反的函數 建議: 若線程需要連接,考慮創建時顯式設置為可連接的。因為并非所有創建線程的實現都是將線程創建為可連接的。 若事先知道線程從不需要連接,考慮創建線程時將其設置為可分離狀態。一些系統資源可能需要釋放。 //這個例子演示了用Pthread join函數去等待線程終止。 //因為有些實現并不是默認創建線程是可連接狀態,例子中顯式地將其創建為可連接的。 #include 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 ⑤線程屬性 linux下線程屬性是可以根據實際項目需要進行設置。 之前我們討論的都是線程的默認屬性,默認屬性已經可以解決大部分線程開發時的需求。 如果需要更高的性能,就需要人為對線程屬性進行配置。 typedef struct { int detachstate; //線程的分離狀態 int schedpolicy; //線程的調度策略 struct sched schedparam;//線程的調度參數 int inheritsched; //線程的繼承性 int scope; //線程的作用域 size_t guardsize; //線程棧末尾的警戒緩沖區大小 int stackaddr_set; //線程棧的設置 void* stackaddr; //線程棧的啟始位置 size_t stacksize; //線程棧大小 }pthread_attr_t; //在上面我們可以看到,關于這個結構體中的相關參數 1 2 3 4 5 6 7 8 9 10 11 12 13 14 默認的屬性為非綁定、非分離、缺省的堆棧、與父進程同樣級別的優先級。 線程屬性設置的一般套路: 第一:定義屬性變量并初始化 pthread_attr_t pthread_attr_init() 第二:調用你想設置的屬性的接口函數 pthread_attr_setxxxxxxxx() 第三:創建線程的時候,第二個參數使用這個屬性 第四:銷毀屬性 pthread_destroy(); 1 2 3 4 5 6 7 8 ⑥敲黑板:棧管理 防止棧問題: POSIX標準并沒有指定線程棧的大小,依賴于實現并隨實現變化。 很容易超出默認的棧大小,常見結果:程序終止或者數據損壞。 安全和可移植的程序應該不依賴于默認的棧限制,但是取而代之的是用pthread_attr_setstacksize分配足夠的棧大小。 pthread_attr_getstackaddr和pthread_attr_setstackaddr函數可以被程序用于將棧設置在指定的內存區域。 pthread_attr_getstacksize (attr, stacksize) pthread_attr_setstacksize (attr, stacksize) pthread_attr_getstackaddr (attr, stackaddr) pthread_attr_setstackaddr (attr, stackaddr) 1 2 3 4 5 6 7 //這個例子演示了如何去查詢和設定線程棧大小。 #include 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 未完待續··· 想了想,線程同步模塊和線程池放在下一個章節吧,我沒有文章字數破W的好習慣?? 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。