mutex (互斥量) (互斥鎖)
多個執行緒同時訪問共享資料時可能會衝突,這跟前面講訊號時所說的可重入性是同樣的問 題。
比如兩個執行緒都要把某個全域性變數增加1,這個操作在某平台需要三條指令完成:
(1). 從記憶體讀變數值到暫存器
(2). 暫存器的值加1
(3)將暫存器的值寫回記憶體
假設四個執行緒在多處理器平台上同時執行這三條指令,則可能導致下圖所示的結果,最後變數只加了一次而非四次。
**如下:
#include
#include
#include
pthread_mutex_t mutex;
static
int g_val = 0;
void *print_msg(void *arg)
return
null;
}int main()
執行結果圖:
對於多執行緒的程式,訪問衝突的問題是很普遍的,解決的辦法是引⼊入互斥鎖(mutex,mutual exclusive lock),獲得鎖的執行緒可以完成「讀-修改-寫」的操作,然後釋放鎖給其它執行緒,沒有獲得鎖的執行緒只能等待而不能訪問共享資料,這樣「讀-修改-寫」三步操作組成乙個原子操作,要麼 都執行,要麼都不執行, 不會執行到中間被打斷,也不會在其它處理器上並行做這個操作。 mutex用pthread_mutex_t型別的變數表示,可以這樣初始化和銷毀:
返回值:成功返回0,失敗返回錯誤號。
pthread_mutex_init函式對mutex做初始化,引數attr設定mutex的屬性,如果attr為null則表示 預設屬性,
用 pthread_mutex_init函式初始化的mutex
如果mutex變數 是靜態分配的(全域性變數 或static變數),也可以⽤用巨集定義pthread_mutex_initializer來初始化,相當於用pthread_mutex_init初始化並且attr引數為null。
mutex的加鎖和解鎖
操作可以⽤用下列函式:
返回值:成功返回0,失敗返回錯誤號。
乙個執行緒可以呼叫pthread_mutex_lock獲得mutex,如果這時另乙個執行緒已經呼叫 pthread_mutex_lock獲得了該mutex,則當前執行緒需要掛起等待,直到另乙個執行緒調⽤用 pthread_mutex_unlock釋放mutex,當前執行緒被喚醒,才能獲得該mutex並繼續執行。
如果乙個執行緒既想獲得鎖,又不想掛起等待,可以呼叫pthread_mutex_trylock,如果mutex已 經被 另乙個執行緒獲得,這個函式會失敗返回ebusy,而不會使執行緒掛起等待。
好,解決上⾯面的問題!
**如下:
源**如下:
#include
#include
#include
pthread_mutex_t mutex;
static
int g_val = 0;
void *print_msg(void *arg)
pthread_mutex_unlock(&mutex); //解鎖
return
null;
}int main()
執行結果:
看到這裡,讀者一定會好奇:mutex的兩個基本操作lock和unlock是如何實現的呢?
假設mutex變數的值為1表示互斥鎖空閒,這時某個程序呼叫lock可以獲得鎖,而mutex的值為0表示互斥鎖已經被 某個執行緒獲得,其它執行緒再呼叫lock只能掛起等待。
那麼lock和unlock的偽**如下:
lock:
if(mutex>0)
//掛起等待
else
unlock:
mutex = 1;
喚醒等待mutex 的執行緒
return
0;
unlock操作中喚醒等待執行緒的步驟可以有不同的實現,可以只喚醒乙個等待執行緒,也可以喚醒 所有等待該mutex的執行緒,然後讓被喚醒的這些執行緒去競爭獲得這個mutex,競爭失敗的執行緒繼續掛起等待。
細心的讀者應該已經看出問題了:對mutex變數的讀取、判斷和修改並不是原子操作。如果兩個執行緒同時呼叫lock,這時mutex是1,兩個執行緒都判斷mutex>0成立,然後其中乙個執行緒置 mutex=0,⽽而 另⼀乙個執行緒並不知道這一情況,也置mutex=0,於是兩個執行緒都以為自己獲得了鎖。
為了實現互斥鎖操作,大多數體系結構都提供了swap或exchange指令,該指令的作用是把暫存器和記憶體單元的資料相交換,由於只有一條指令,保證了原子性,即使是多處理器平台,訪問記憶體的匯流排週期也有先後,乙個處理器上的交換指令執行時另乙個處理器的交換指令只能等待匯流排週期。
「掛起等待」和「喚醒等待執行緒」的操作如何實現?每個mutex有乙個等待佇列,乙個執行緒要在mutex上掛起等待,首先在把自己加入等待佇列中,然後置執行緒狀態為睡眠,然後呼叫排程器函式切換到別的執行緒。乙個執行緒要喚醒等待佇列中的其它執行緒,只需從等待佇列中取出一 項,把它的狀態從睡眠改為就緒,加入就緒佇列,那麼下次排程器函式執行時就有可能切換到被喚醒的執行緒。 一般情況下,如果同乙個執行緒先後兩次呼叫lock,在第二次呼叫時,由於鎖已經被占用,該執行緒會掛起等待別的執行緒釋放鎖,然而鎖正是被自己占用著的,該執行緒又被掛起而沒有機會釋放鎖, 因此就永遠處於掛起等待狀態了,這叫做死鎖(deadlock)。
另一種典型的死鎖情形是這樣:線 程a獲得了鎖1, 執行緒b獲得了鎖2, 這時執行緒a呼叫lock試圖獲得鎖2, 結果是需要掛起等待執行緒 b釋放鎖2, 而這時執行緒b也呼叫lock試圖獲得鎖1, 結果是需要掛起等待執行緒a釋放鎖1, 於是線 程a和b都永遠處於掛起狀態了。
寫程式時應該盡量避免同時獲得多個鎖,如果一定有必要這麼做,則有乙個原則:如果所有執行緒 在需要多個鎖時都按相同的先後順序(常見的是按mutex變數的位址順序)獲得鎖,則不會出現死鎖。⽐比如乙個程式中用到鎖1、鎖2、鎖3,它們所對應的mutex變數的位址是鎖1《鎖2《鎖3, 那麼所有執行緒在需要同時獲得2個或3個鎖時都應該按鎖1、鎖2、鎖3的順序獲得。
如果要為所有的鎖確定乙個先後順序⽐比較困難,則應該盡量使⽤用pthread_mutex_trylock調⽤用代替pthread_mutex_lock 調 ⽤用,以免死鎖。
執行緒同步與互斥
與多程序互動一樣,各個不同的執行緒之間也會存在資源的共享問題。為了解決多執行緒之間對資源訪問的同步和互斥問題,我們需要了解執行緒同步機制。第一種機制 互斥鎖 互斥鎖是一種簡單的加鎖方法,可以使單個執行緒進行對資源訪問的原子操作。互斥鎖的基本操作就是加鎖和解鎖。互斥鎖主要包含以下函式 1 初始化函式 ...
執行緒同步與互斥
1.執行緒互斥 執行緒互斥是指某一資源同時只允許乙個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。同步就是協同步調,按預定的先後次序進行執行。如 你說完,我再說。同 字從字面上容易理解為一起動作 其實不是,同 字應是指協同 協助 互相配合。如程序 執行...
執行緒同步與互斥 互斥鎖
在多工作業系統中,同時執行的多個任務可能都需要使用同一種資源。這個過程有點類似於,公司部門裡,我在使用著印表機列印東西的同時 還沒有列印完 別人剛好也在此刻使用印表機列印東西,如果不做任何處理的話,列印出來的東西肯定是錯亂的。下面我們用程式模擬一下這個過程,執行緒一需要列印 hello 執行緒二需要...