互斥鎖mutex的簡(jiǎn)單實(shí)現(xiàn)

mutex一般用于為一段代碼加鎖,以保證這段代碼的原子性(atomic)操作满力,即:要么不執(zhí)行這段代碼拢肆,要么將這段代碼全部執(zhí)行完畢。

例如耐亏,最簡(jiǎn)單的并發(fā)沖突問題就是一個(gè)變量自增1:

balance = balance + 1;

表面看這是一條語句徊都,可是在背后的匯編中我們可以看到,指令集操作過程中會(huì)引入中間變量來保存右邊的值广辰,進(jìn)而這個(gè)操作至少會(huì)被擴(kuò)充為:

int tmp = balance + 1;
balance = tmp;

這就需要一把互斥鎖(mutual exclusive, mutex)將這段代碼給鎖住暇矫,使其達(dá)到任何一個(gè)線程“要么全部執(zhí)行上述代碼,要么不執(zhí)行這段代碼”的效果择吊。這個(gè)用法可以表示為:

lock_t mutex;
...
lock(&mutex)
    balance = balance + 1;
unlock(&mutex);

那么李根,一個(gè)自然的問題便是,我如何實(shí)現(xiàn)上面的這個(gè)lock()函數(shù)呢几睛?

乍一看這個(gè)問題是非常復(fù)雜的房轿,特別是考慮到它能夠被適用于各種代碼的各種情況。但經(jīng)過各種簡(jiǎn)化,這個(gè)lock()實(shí)現(xiàn)囱持,可以通過幾個(gè)test和set的組合得以實(shí)現(xiàn)夯接。

例如,

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;
}

我第一次看到這個(gè)算法的時(shí)候非常驚訝纷妆,一個(gè)本來極其復(fù)雜的問題就這么優(yōu)雅地被解決了盔几。它僅僅涉及到對(duì)條件的檢驗(yàn)和變量的復(fù)制,然后整個(gè)問題就這么輕而易舉地被攻破了掩幢。

當(dāng)然逊拍,我并沒能看到上述代碼的“坑”,也即是必須依靠指令集級(jí)別的支持才能真正做到atomic际邻。這同樣說明了并發(fā)程序的困難芯丧,稍微不注意便會(huì)調(diào)入一個(gè)萬劫不復(fù)的坑里,并且你還不知道哪里出錯(cuò)了世曾。

上述極端優(yōu)雅的代碼缨恒,有一個(gè)隱藏的坑,那便是在lock()函數(shù)的實(shí)現(xiàn)里度硝,while循環(huán)那一段其實(shí)是可以被亂入的肿轨。

假設(shè)thread A是第一個(gè)運(yùn)行到此的線程寿冕,那么它得到的mutex->flag就肯定是0蕊程,于是它繼續(xù)跳出循環(huán)往下運(yùn)行,希望通過下面的mutex->flag = 1來持有鎖驼唱,使得其它線程在檢測(cè)while循環(huán)時(shí)為真藻茂,進(jìn)而進(jìn)入循環(huán)的等待狀態(tài)。

可如果在A執(zhí)行到這個(gè)賦值為1的語句之前玫恳,又有另外一個(gè)thread B運(yùn)行到了這個(gè)while循環(huán)部分辨赐,由于mutex->flag還未被賦值為1,B同樣可以跳出while京办,從而跟A一樣拿到這把鎖掀序!這就出現(xiàn)了沖突。

那怎么辦呢惭婿?仔細(xì)后可以發(fā)現(xiàn)不恭,其實(shí)關(guān)鍵問題就在于:

  • 對(duì)mutex->flag的檢測(cè)
  • 對(duì)mutex->flag的賦值

這兩個(gè)操作必須是不被干擾的,也就是它必須是atomic的财饥,要么這兩段代碼不被執(zhí)行换吧,要么這兩段代碼被不中斷地完整執(zhí)行。

這就需要借助CPU指令集的幫助钥星,來保證上述兩條語句的atomic操作沾瓦,也即是著名的TestAndSet()操作。

int TestAndSet(int *ptr, int new) {
    int old = *ptr;
    *ptr = new;
    return old;
}

CPU的指令集,并不需要支持繁復(fù)的各種atomic操作贯莺。僅僅支持上面這個(gè)函數(shù)风喇,各種互斥加鎖的情形,便都能夠被涵蓋乖篷。

此時(shí)响驴,在回到我們最開始的那個(gè)優(yōu)雅的lock()實(shí)現(xiàn),就可以將其改造為:

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()實(shí)現(xiàn)里不是還缺少一行mutex->flag = 1;么豁鲤?可其實(shí)呢,它已經(jīng)被整合到了TestAndSet()函數(shù)中鲸沮。

這樣的支持TestAndSet()的實(shí)現(xiàn)琳骡,便是最簡(jiǎn)單的spin lock,彈簧鎖讼溺。之所以叫彈簧鎖楣号,那是因?yàn)樵诟黝愭i當(dāng)中,彈簧鎖就是最初的被投入工業(yè)使用的最簡(jiǎn)單的實(shí)現(xiàn)技術(shù)怒坯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炫狱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剔猿,更是在濱河造成了極大的恐慌视译,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件归敬,死亡現(xiàn)場(chǎng)離奇詭異酷含,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)汪茧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門椅亚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舱污,你說我怎么就攤上這事呀舔。” “怎么了扩灯?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵媚赖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我驴剔,道長(zhǎng)省古,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任丧失,我火速辦了婚禮豺妓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己琳拭,他們只是感情好训堆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著白嘁,像睡著了一般坑鱼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上絮缅,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天鲁沥,我揣著相機(jī)與錄音,去河邊找鬼耕魄。 笑死画恰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吸奴。 我是一名探鬼主播允扇,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼则奥!你這毒婦竟也來了考润?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤读处,失蹤者是張志新(化名)和其女友劉穎糊治,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體档泽,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俊戳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年揖赴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了馆匿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡燥滑,死狀恐怖渐北,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铭拧,我是刑警寧澤赃蛛,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站搀菩,受9級(jí)特大地震影響呕臂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肪跋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一歧蒋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦谜洽、人聲如沸萝映。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽序臂。三九已至,卻和暖如春实束,著一層夾襖步出監(jiān)牢的瞬間奥秆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工咸灿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吭练,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓析显,卻偏偏與公主長(zhǎng)得像鲫咽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谷异,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容