深入Phtread 二 執行緒的同步 Mutex

2021-06-23 00:14:43 字數 2914 閱讀 2378

並行的世界,沒有同步,就失去了秩序,就會亂作一團!試想,交通沒有紅綠燈,生產線產品裝配沒有一定的順序... 結果是顯而易見的。多個執行緒也需要同步,否則程式執行起來結果不可**,這是我們最不能容忍的。交通的同步機制就是紅綠燈,pthread提供了互斥量(mutex)和條件變數(condition variables)兩種機制去同步執行緒。

不變數,臨界區和判定條件

互斥量(mutex)

建立和銷毀互斥量

鎖定和解鎖

調整mutex大小

使用多個mutex

鎖定鏈

不變數(invariant):程式所做的一些假設,特別是指變數之間的關係。如:乙個queue,有頭節點,和其它資料節點,這些元素之間的連線關係就是不變數。當程式裡面不變數遭受破壞時,後果往往是很嚴重的,輕則資料出錯,重則程式直接崩潰。

臨界區(critical section):處理共享資料的一段**。

判定條件(predicates):描述不變數狀態的邏輯表示式。

一般,多個執行緒之間都會共享一些資料,當多個執行緒同時訪問操作這些共享資料時。問題出來了,乙個執行緒正在修改資料時,另外乙個可能也去操作這些資料,結果就會變得不一致了。如(gv=0是共享的資料):

執行緒a:a = gv; gv = a + 10; 

執行緒b: b = gv; gv = a + 100;

可能發生a執行完a=gv(0)時,b開始執行b=gv(0); gv=a+100,此時gv=100,然後a執行gv=a+10,最後gv=10。並不是我們要的結果,我們的想法是兩個執行緒併發的給gv加上乙個值,期望結果110。^_^ 若這是你銀行卡的餘額,若沒有同步,那就慘了(你往卡里打錢,你有個朋友也同時往你卡里匯錢,很有可能餘額只僅加上一方打的)。

互斥量就是為了解決這種問題而設計的,它是dijkstra訊號量的一種特殊形式。它使得執行緒可以互斥地訪問共享資料。如:

上圖展示了三個執行緒共享乙個互斥量,位於矩形中心線下方的執行緒鎖定了該互斥量;位於中心線上方且在矩形範圍內的執行緒等待該互斥量被解鎖,出於阻塞狀態,在矩形外面的執行緒正常執行。剛開始,mutex是解鎖的,執行緒1成功將其鎖定,據為己有,因為並沒有其它執行緒擁有它。然後,執行緒2嘗試去鎖定,發現被執行緒1占用,所以阻塞於此,等到執行緒1解鎖了該mutex,執行緒2立馬將mutex鎖定。過了會,執行緒3嘗試去鎖定mutex,由於mutex被鎖定,所以阻塞於此。執行緒1呼叫pthread_mutex_trylock嘗試去鎖定個mutex,發現該mutex被鎖定,自己返回繼續執行,並沒有阻塞。繼續執行緒2解鎖,執行緒3鎖定成功,最後執行緒3完成任務解鎖mutex。

pthread_mutex_t mutex = pthread_mutex_initializer;

int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* attr);

int pthread_mutex_destroy(pthread_mutex_t* mutex);

不要嘗試去使用複製的的mutex,結果未定義。

靜態建立,當mutex以extern或者static儲存時,可以用pthread_mutex_initializer初始化,此時該mutex使用預設屬性。

動態建立,往往使用mutex時,都會將它和共享資料綁在一起,此時就需要pthread_mutex_init去動態初始化了,記得用完後pthread_mutex_destroy。

原則見上面。

int pthread_mutex_lock(pthread_mutex_t* mutex);

int pthread_mutex_trylock(pthread_mutex_t* mutex);

int pthread_mutex_unlock(pthread_mutex_t* mutex);

mutex應該多大?這裡的大小是相對的,如mutex鎖定到解鎖之間的**只有一行,比起有10行的就小了。 原則是:盡可能大,但不要太大(as big as neccessary, but no bigger)。考慮下面的因素:1> mutex並不是免費的,是有開銷的,不要太小了,太小了程式只忙於鎖定和解鎖了。2> mutex鎖定的區域是線性執行的,若太大了,沒有發揮出併發的優越性。

3> 自己掂量1和2,根據實際情況定,或者嘗試著去做。

使用多個mutex一定要注意,防止死鎖(deadlock)發生。下面是乙個典型死鎖:

執行緒a:pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); ...

執行緒b:pthread_mutex_lock(&mutex_b); pthread_mutex_lock(&mutex_a); ...

存在這種可能,執行緒a執行了第一句,鎖定了mutex_a;然後執行緒開始執行第一句鎖定mutex_b;然後他們互相等待解鎖mutex,a等mutex_b被解鎖,b等mutex_a被解鎖,不肯讓步,出於死鎖狀態。

解決死鎖的方法:

固定鎖定順序(fixed locking hierarchy):鎖定mutex的順序固定。

執行緒a:pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); ...

執行緒b:pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); ...

解鎖順序不會引起死鎖.

一般用於遍歷資料結果(樹,鍊錶),乙個用於鎖定指標,乙個鎖定資料。

形如:pthread_mutex_lock(&mutex_a);

pthread_mutex_lock(&mutex_b);

...pthread_mutex_unlock(&mutex_a)

...pthread_mutex_unlock(&mutex_b)

注意,鎖定鏈往往會出現大量的鎖定和解鎖操作,有時會得不償失。

二 執行緒狀態

新建狀態 new 用new關鍵字建立乙個執行緒物件後,該執行緒物件就處於新生狀態。處於新生狀態的執行緒有自己的記憶體空間,通過呼叫start方法進入就緒狀態。就緒狀態 runnable 處於就緒狀態的執行緒已經具備了執行條件,但是還沒有被分配到cpu,處於 執行緒就緒佇列 等待系統為其分配cpu。就...

執行緒二 執行緒的好處和壞處

一 執行緒的好處 想一下,在一台機器中有多個程式並行執行,和只能有乙個執行緒執行的情況,對比一下,1 讓多個處理器都發揮用處。2 能非同步的方式處理 事件 其他執行緒阻塞,不會影響我當前執行緒的執行 3 程式設計變得簡單。程式序列越少,程式越簡單,我們可以用多個簡單的執行緒完成複雜的邏輯。就像多個人...

java多執行緒 二 執行緒的互斥

多執行緒相對於單執行緒而言,大大的提高了硬體cpu的使用率,提高了處理的速度。任何事物帶來的都是兩面性的,多執行緒為我們帶來效能提高的同時也帶來了許多的安全性問題。說互斥之前,先說一下什麼是互斥,舉個列子,一天去atm機取錢,如果沒有互斥的話,你正取著錢突然有個人衝進來把你的錢搶走了。這時候你想,要...