互斥鎖mutex的簡單實現

2021-09-13 00:15:37 字數 2154 閱讀 8468

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 方法就...