mutex一般用於為一段**加鎖,以保證這段**的原子性(atomic)操作,即:要麼不執行這段**,要麼將這段**全部執行完畢。
例如,最簡單的併發衝突問題就是乙個變數自增1:
balance = balance + 1;
表面看這是一條語句,可是在背後的彙編中我們可以看到,指令集操作過程中會引入中間變數來儲存右邊的值,進而這個操作至少會被擴充為:
int tmp = balance + 1;
balance = tmp;
這就需要一把互斥鎖(mutual exclusive, mutex)將這段**給鎖住,使其達到任何乙個執行緒「要麼全部執行上述**,要麼不執行這段**」的效果。這個用法可以表示為:
lock_t mutex;
...lock(&mutex)
balance = balance + 1;
unlock(&mutex);
那麼,乙個自然的問題便是,我如何實現上面的這個lock()
函式呢?
乍一看這個問題是非常複雜的,特別是考慮到它能夠被適用於各種**的各種情況。但經過各種簡化,這個lock()
實現,可以通過幾個test和set的組合得以實現。
例如,
typedef struct __lock_t lock_t;
void init(lock_t *mutex)
void lock(lock_t *mutex)
void unlock(lock_t *mutex)
我第一次看到這個演算法的時候非常驚訝,乙個本來極其複雜的問題就這麼優雅地被解決了。它僅僅涉及到對條件的檢驗和變數的複製,然後整個問題就這麼輕而易舉地被攻破了。
當然,我並沒能看到上述**的「坑」,也即是必須依靠指令集級別的支援才能真正做到atomic。這同樣說明了併發程式的困難,稍微不注意便會調入乙個萬劫不復的坑里,並且你還不知道**出錯了。
上述極端優雅的**,有乙個隱藏的坑,那便是在lock()
函式的實現裡,while
迴圈那一段其實是可以被亂入的。
假設thread a是第乙個執行到此的執行緒,那麼它得到的mutex->flag
就肯定是0,於是它繼續跳出迴圈往下執行,希望通過下面的mutex->flag = 1
來持有鎖,使得其它執行緒在檢測while
迴圈時為真,進而進入迴圈的等待狀態。
可如果在a執行到這個賦值為1的語句之前,又有另外乙個thread b執行到了這個while
迴圈部分,由於mutex->flag
還未被賦值為1,b同樣可以跳出while
,從而跟a一樣拿到這把鎖!這就出現了衝突。
那怎麼辦呢?仔細後可以發現,其實關鍵問題就在於:
這兩個操作必須是不被干擾的,也就是它必須是atomic的,要麼這兩段**不被執行,要麼這兩段**被不中斷地完整執行。
這就需要借助cpu指令集的幫助,來保證上述兩條語句的atomic操作,也即是著名的testandset()
操作。
int testandset(int *ptr, int new)
cpu的指令集,並不需要支援繁複的各種atomic操作。僅僅支援上面這個函式,各種互斥加鎖的情形,便都能夠被涵蓋。
此時,在回到我們最開始的那個優雅的lock()
實現,就可以將其改造為:
typedef struct __lock_t lock_t;
void init(lock_t *lock)
void lock(lock_t *mutex)
void unlock(lock_t *lock)
上述**極其精巧。乍一看在lock()
實現裡不是還缺少一行mutex->flag = 1;
麼?可其實呢,它已經被整合到了testandset()
函式中。
這樣的支援testandset()
的實現,便是最簡單的spin lock,彈簧鎖。之所以叫彈簧鎖,那是因為在各類鎖當中,彈簧鎖就是最初的被投入工業使用的最簡單的實現技術。
互斥鎖mutex的簡單實現
mutex一般用於為一段 加鎖,以保證這段 的原子性 atomic 操作,即 要麼不執行這段 要麼將這段 全部執行完畢。例如,最簡單的併發衝突問題就是乙個變數自增1 balance balance 1 表面看這是一條語句,可是在背後的彙編中我們可以看到,指令集操作過程中會引入中間變數來儲存右邊的值,...
互斥鎖mutex簡單應用
寫了段小 學習互斥鎖的作用。互斥鎖的作用 在程式設計中,引入了物件互斥鎖的概念,來保證共享資料操作的完整性。思路是用2個執行緒對同乙個記憶體位置buffer,進行寫和讀,為了防止2個執行緒同時對buffer進行修改,用同一把互斥鎖來限制。寫操作簡化為把buffer修改為 w 讀簡化為把buffer修...
C 實現mutex 互斥鎖
思路 實現mutex最重要的就是實現它的lock 方法和unlock 方法。我們儲存乙個全域性變數flag,flag 1表明該鎖已經鎖住,flag 0表明鎖沒有鎖住。實現lock 時,使用乙個while迴圈不斷檢測flag是否等於1,如果等於1就一直迴圈。然後將flag設定為1 unlock 方法就...