mutex通常用於爲一段代碼加鎖,以保證這段代碼的原子性(atomic)操做,即:要麼不執行這段代碼,要麼將這段代碼所有執行完畢。算法
例如,最簡單的併發衝突問題就是一個變量自增1:併發
balance = balance + 1;
表面看這是一條語句,但是在背後的彙編中咱們能夠看到,指令集操做過程當中會引入中間變量來保存右邊的值,進而這個操做至少會被擴充爲:函數
int tmp = balance + 1; balance = tmp;
這就須要一把互斥鎖(mutual exclusive, mutex)將這段代碼給鎖住,使其達到任何一個線程「要麼所有執行上述代碼,要麼不執行這段代碼」的效果。這個用法能夠表示爲:atom
lock_t mutex; ... lock(&mutex) balance = balance + 1; unlock(&mutex);
那麼,一個天然的問題即是,我如何實現上面的這個lock()
函數呢?線程
乍一看這個問題是很是複雜的,特別是考慮到它可以被適用於各類代碼的各類狀況。但通過各類簡化,這個lock()
實現,能夠經過幾個test和set的組合得以實現。code
例如,it
typedef struct __lock_t { int flag; } lock_t; void init(lock_t *mutex) { // 0: lock is available // 1: lock is held mutex->flag = 0; } void lock(lock_t *mutex) { while (mutex->flag == 1) { // Test the flag. ; // Wait the lock mutex->flag = 1; // Set the lock, i.e. start to hold lock } void unlock(lock_t *mutex) { mutex->flag = 0; }
我第一次看到這個算法的時候很是驚訝,一個原本極其複雜的問題就這麼優雅地被解決了。它僅僅涉及到對條件的檢驗和變量的複製,而後整個問題就這麼垂手可得地被攻破了。class
固然,我並沒能看到上述代碼的「坑」,也便是必須依靠指令集級別的支持才能真正作到atomic。這一樣說明了併發程序的困難,稍微不注意便會調入一個萬劫不復的坑裏,而且你還不知道哪裏出錯了。thread
上述極端優雅的代碼,有一個隱藏的坑,那即是在lock()
函數的實現裏,while
循環那一段實際上是能夠被亂入的。test
假設thread A是第一個運行到此的線程,那麼它獲得的mutex->flag
就確定是0,因而它繼續跳出循環往下運行,但願經過下面的mutex->flag = 1
來持有鎖,使得其它線程在檢測while
循環時爲真,進而進入循環的等待狀態。
可若是在A執行到這個賦值爲1的語句以前,又有另一個thread B運行到了這個while
循環部分,因爲mutex->flag
還未被賦值爲1,B一樣能夠跳出while
,從而跟A同樣拿到這把鎖!這就出現了衝突。
那怎麼辦呢?仔細後能夠發現,其實關鍵問題就在於:
mutex->flag
的檢測mutex->flag
的賦值這兩個操做必須是不被幹擾的,也就是它必須是atomic的,要麼這兩段代碼不被執行,要麼這兩段代碼被不中斷地完整執行。
這就須要藉助CPU指令集的幫助,來保證上述兩條語句的atomic操做,也便是著名的TestAndSet()
操做。
int TestAndSet(int *ptr, int new) { int old = *ptr; *ptr = new; return old; }
CPU的指令集,並不須要支持繁複的各類atomic操做。僅僅支持上面這個函數,各類互斥加鎖的情形,便都可以被涵蓋。
此時,在回到咱們最開始的那個優雅的lock()
實現,就能夠將其改造爲:
typedef struct __lock_t { int flag; } lock_t; void init(lock_t *lock) { // 0: lock is available // 1: lock is held mutex->flag = 0; } void lock(lock_t *mutex) { while (TestAndSet(&lock_t->flag, 1) == 1) { ; } void unlock(lock_t *lock) { lock->flag = 0; }
上述代碼極其精巧。乍一看在lock()
實現裏不是還缺乏一行mutex->flag = 1;
麼?可其實呢,它已經被整合到了TestAndSet()
函數中。
這樣的支持TestAndSet()
的實現,即是最簡單的spin lock,彈簧鎖。之因此叫彈簧鎖,那是由於在各種鎖當中,彈簧鎖就是最初的被投入工業使用的最簡單的實現技術。