99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深入了解互斥鎖、條件變量、讀寫鎖以及自旋鎖

Linux愛好者 ? 來源:果凍蝦仁 ? 作者:果凍蝦仁 ? 2021-11-01 10:02 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

鎖是一個(gè)常見的同步概念,我們都聽說過加鎖(lock)或者解鎖(unlock),當(dāng)然學(xué)術(shù)一點(diǎn)的說法是獲?。?a target="_blank">acquire)和釋放(release)。

恰好pthread包含這幾種鎖的API,而C++11只包含其中的部分。接下來我主要通過pthread的API來展開本文。

mutex(互斥量)

mutex(mutual exclusive)即互斥量(互斥體)。也便是常說的互斥鎖。

盡管名稱不含lock,但是稱之為鎖,也是沒有太大問題的。mutex無(wú)疑是最常見的多線程同步方式。其思想簡(jiǎn)單粗暴,多線程共享一個(gè)互斥量,然后線程之間去競(jìng)爭(zhēng)。得到鎖的線程可以進(jìn)入臨界區(qū)執(zhí)行代碼。

//聲明一個(gè)互斥量
pthread_mutex_tmtx;
//初始化
pthread_mutex_init(&mtx,NULL);
//加鎖
pthread_mutex_lock(&mtx);
//解鎖
pthread_mutex_unlock(&mtx);
//銷毀
pthread_mutex_destroy(&mtx);

mutex是睡眠等待(sleep waiting)類型的鎖,當(dāng)線程搶互斥鎖失敗的時(shí)候,線程會(huì)陷入休眠。優(yōu)點(diǎn)就是節(jié)省CPU資源,缺點(diǎn)就是休眠喚醒會(huì)消耗一點(diǎn)時(shí)間。另外自從Linux 2.6版以后,mutex完全用futex的API實(shí)現(xiàn)了,內(nèi)部系統(tǒng)調(diào)用的開銷大大減小。

值得一提的是,pthread的鎖一般都有一個(gè)trylock的函數(shù),比如對(duì)于互斥量:

ret=pthread_mutex_trylock(&mtx);
if(0==ret){//加鎖成功
...
pthread_mutex_unlock(&mtx);
}elseif(EBUSY==ret){//鎖正在被使用;
...
}

pthread_mutex_trylock用于以非阻塞的模式來請(qǐng)求互斥量。就好比各種IO函數(shù)都有一個(gè)noblock的模式一樣,對(duì)于加鎖這件事也有類似的非阻塞模式。

當(dāng)線程嘗試加鎖時(shí),如果鎖已經(jīng)被其他線程鎖定,該線程就會(huì)阻塞住,直到能成功acquire。但有時(shí)候我們不希望這樣。

pthread_mutex_trylock在被其他線程鎖定時(shí),會(huì)返回特殊錯(cuò)誤碼。加鎖成返回0,僅當(dāng)成功但時(shí)候,我們才能解鎖在后面進(jìn)行解鎖操作!

C++11開始引入了多線程庫(kù),其中也包含了互斥鎖的API:std::muxtex

此外,依據(jù)同一線程是否能多次加鎖,把互斥量又分為如下兩類:

  • 是:稱為『遞歸互斥量』recursive mutex ,也稱『可重入鎖』reentrant lock
  • 否:即『非遞歸互斥量』non-recursive mute),也稱『不可重入鎖』non-reentrant mutex

若同一線程對(duì)非遞歸的互斥量多次加鎖,可能會(huì)造成死鎖。遞歸互斥量則無(wú)此風(fēng)險(xiǎn)。C++11中有遞歸互斥量的API:std::recursive_mutex。對(duì)于pthread則可以通過給mutex添加PTHREAD_MUTEX_RECURSIVE 屬性的方式來使用遞歸互斥量:

//聲明一個(gè)互斥量
pthread_mutex_tmtx;
//聲明一個(gè)互斥量的屬性變量
pthread_mutexattr_tmtx_attr;

//初始化互斥量的屬性變量
pthread_mutexattr_init(&mtx_attr);
//設(shè)置遞歸互斥量的屬性
pthread_mutexattr_settype(&mtx_attr,PTHREAD_MUTEX_RECURSIVE);

//把屬性賦值給互斥量
pthread_mutext_init(&mtx,&mutext_attr);

然而對(duì)于遞歸互斥量或者說可重入鎖的使用則需要克制。Stevens大神生前在《APUE》中說『使用好它是十分tricky的,僅當(dāng)沒有其他解決方案時(shí)才使用』。

可重入鎖這個(gè)概念和稱呼的走俏多半是Java語(yǔ)言的功勞。

condition variable(條件變量)

請(qǐng)注意條件變量不是鎖,它是一種線程間的通訊機(jī)制,并且?guī)缀蹩偸呛突コ饬恳黄鹗褂玫摹K曰コ饬亢蜅l件變量二者一般是成套出現(xiàn)的。比如C++11中也有條件變量的API:std::condition_variable。

對(duì)于pthread:

//聲明一個(gè)互斥量
pthread_mutex_tmtx;
//聲明一個(gè)條件變量
pthread_cond_tcond;
...

//初始化
pthread_mutex_init(&mtx,NULL);
pthread_cond_init(&cond,NULL);

//加鎖
pthread_mutex_lock(&mtx);
//加鎖成功,等待條件變量觸發(fā)
pthread_cond_wait(&cond,&mtx);

...
//加鎖
pthread_mutex_lock(&mtx);
pthread_cond_signal(&cond);
...
//解鎖
pthread_mutex_unlock(&mtx);
//銷毀
pthread_mutex_destroy(&mtx)

pthread_cond_wait函數(shù)會(huì)把條件變量和互斥量都傳入。并且多線程調(diào)用的時(shí)候條件變量和互斥量一定要一一對(duì)應(yīng),不能一個(gè)條件變量在不同線程中wait的時(shí)候傳入不同的互斥量。否則是未定義結(jié)果。

關(guān)于是先解鎖互斥量還是先進(jìn)行條件變量的通知,是另外一個(gè)比較大的議題。有種論斷說:先解鎖互斥量再通知條件變量可以減少多余的上下文切換,進(jìn)而提高效率。這種說法是基于一種實(shí)現(xiàn)假設(shè):先通知條件變量,再解鎖。

可能讓其他等待條件變量的線程被喚醒了,但是此時(shí)互斥量還沒解鎖,從而再次陷入休眠。然而對(duì)于另外一些實(shí)現(xiàn),比如Linux系統(tǒng),則通過等待變形(wait morphing)解決了這一問題。所以先通知再解鎖也沒用問題。

另外在使用條件變量的過程中有個(gè)稍微違反直覺的寫法:那就是使用while而不是if來做判斷狀態(tài)是否滿足。這樣做的原因有二:

  1. 避免驚群;
  2. 避免某些情況下線程被虛假喚醒(即沒有pthread_cond_signal就解除了阻塞)。

比如半同步/半reactor的網(wǎng)絡(luò)模型中,在工作線程消費(fèi)fd隊(duì)列的時(shí)候:

while(1){
if(pthread_mutex_lock(&mtx)!=0){//加鎖
...//異常邏輯
}
while(!queue.empty()){
if(pthread_cond_wait(&cond,&mtx)!=0){
...//異常邏輯
}
}
autodata=queue.pop();
if(pthread_mutex_unlock(&mtx)!=0){//解鎖
...//異常邏輯
}
process(data);//處理流程,業(yè)務(wù)邏輯
}

read-write lock(讀寫鎖)

顧名思義『讀寫鎖』就是對(duì)于臨界區(qū)區(qū)分讀和寫。在讀多寫少的場(chǎng)景下,不加區(qū)分的使用互斥量顯然是有點(diǎn)浪費(fèi)的。此時(shí)便該上演讀寫鎖的拿手好戲。

讀寫鎖有一個(gè)別稱叫『共享-獨(dú)占鎖』。不過單看『共享-獨(dú)占鎖』或者『讀寫鎖』這兩個(gè)名稱,其實(shí)并未區(qū)分對(duì)于讀和寫,到底誰(shuí)共享,誰(shuí)獨(dú)占??赡軙?huì)讓人誤以為讀寫鎖是一種更為泛化的稱呼,其實(shí)不是。讀寫鎖的含義是準(zhǔn)確的:是一種 讀共享,寫?yīng)氄嫉逆i。

讀寫鎖的特性:

  • 當(dāng)讀寫鎖被加了寫鎖時(shí),其他線程對(duì)該鎖加讀鎖或者寫鎖都會(huì)阻塞(不是失?。?。
  • 當(dāng)讀寫鎖被加了讀鎖時(shí),其他線程對(duì)該鎖加寫鎖會(huì)阻塞,加讀鎖會(huì)成功。

因而適用于多讀少寫的場(chǎng)景。

//聲明一個(gè)讀寫鎖
pthread_rwlock_trwlock;
...
//在讀之前加讀鎖
pthread_rwlock_rdlock(&rwlock);

...共享資源的讀操作

//讀完釋放鎖
pthread_rwlock_unlock(&rwlock);

//在寫之前加寫鎖
pthread_rwlock_wrlock(&rwlock);

...共享資源的寫操作

//寫完釋放鎖
pthread_rwlock_unlock(&rwlock);

//銷毀讀寫鎖
pthread_rwlock_destroy(&rwlock);

其實(shí)加讀鎖和加寫鎖這兩個(gè)說法可能會(huì)造成誤導(dǎo),讓人誤以為是有兩把鎖,其實(shí)讀寫鎖是一個(gè)鎖。所謂加讀鎖和加寫鎖,準(zhǔn)確的說法可能是『給讀寫鎖加讀模式的鎖定和加寫模式的鎖定』。

讀寫鎖和互斥量一樣也有trylock函數(shù),也是以非阻塞地形式來請(qǐng)求鎖,不會(huì)導(dǎo)致阻塞。

pthread_rwlock_tryrdlock(&rwlock)
pthread_rwlock_trywrlock(&rwlock)

C++11中有互斥量、條件變量但是并沒有引入讀寫鎖。而在C++17中出現(xiàn)了一種新鎖:std::shared_mutex。用它可以模擬實(shí)現(xiàn)出讀寫鎖。demo代碼可以直接參考cppreference:

https://en.cppreference.com/w/cpp/thread/shared_mutex

另外多讀少寫的場(chǎng)景有些特殊場(chǎng)景,可以用特殊的數(shù)據(jù)結(jié)構(gòu)減少鎖使用:

  • 多讀單寫的線性數(shù)據(jù)。用數(shù)組實(shí)現(xiàn)環(huán)形隊(duì)列,避免vector等動(dòng)態(tài)擴(kuò)張的數(shù)據(jù)結(jié)構(gòu),寫在結(jié)尾,由于單寫因而可以不加鎖;讀在開頭,由于多讀(避免重復(fù)消費(fèi))所以需要加一下鎖(互斥量就行)。
  • 多讀單寫的KV。可以使用雙緩沖(double buffer)的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)。double buffer同名的概念比較多,這里指的是foreground 和 backgroud 兩個(gè)buffer進(jìn)行切換的『0 - 1切換』技術(shù)。比如實(shí)現(xiàn)動(dòng)態(tài)加載(熱加載)配置文件的時(shí)候??赡軙?huì)在切換間隙加一個(gè)短暫的互斥量,但是基本可以認(rèn)為是lock free的。

我一張口,你就會(huì)發(fā)現(xiàn):無(wú)非是空間換時(shí)間的老套路了。

spinlock(自旋鎖)

自旋之名頗為玄妙,第一次聽聞常讓人略覺高大。但和無(wú)數(shù)個(gè)好似『故意把簡(jiǎn)單概念復(fù)雜化』的計(jì)算機(jī)術(shù)語(yǔ)一樣,自旋鎖的本質(zhì)簡(jiǎn)單的難以置信。

要了解自旋鎖,首先了解自旋。什么是自旋(spin)呢?更為通俗的一個(gè)詞是『忙等待』(busy waiting)。最最通俗的一個(gè)理解,其實(shí)就是死循環(huán)……。

單看使用方法和使用互斥量的代碼是差不多的。只不過自旋鎖不會(huì)引起線程休眠。當(dāng)共享資源的狀態(tài)不滿足的時(shí)候,自旋鎖會(huì)不停地循環(huán)檢測(cè)狀態(tài)。因?yàn)椴粫?huì)陷入休眠,而是忙等待的方式也就不需要條件變量。

這是優(yōu)點(diǎn)也是缺點(diǎn)。不休眠就不會(huì)引起上下文切換,但是會(huì)比較浪費(fèi)CPU。

//聲明一個(gè)自旋鎖變量
pthread_spinlock_tspinlock;

//初始化
pthread_spin_init(&spinlock,0);

//加鎖
pthread_spin_lock(&spinlock);

//解鎖
pthread_spin_unlock(&spinlock);

//銷毀
pthread_spin_destroy(&spinlock);

pthread_spin_init函數(shù)的第二個(gè)參數(shù)名為pshared(int類型)。表示的是是否能進(jìn)程間共享自旋鎖。這被稱之為Thread Process-Shared Synchronization?;コ饬康耐ㄟ^屬性也可以把互斥量設(shè)置成進(jìn)程間共享的。pshared有兩個(gè)枚舉值:

  • PTHREAD_PROCESS_PRIVATE:僅同進(jìn)程下讀線程可以使用該自旋鎖
  • PTHREAD_PROCESS_SHARED:不同進(jìn)程下的線程可以使用該自旋鎖

Linux上的glibc中這兩個(gè)枚舉值分別是01Mac上不是)。所以通常也會(huì)看到直接傳0的代碼。你可能覺得不使用宏,直接用數(shù)字硬編碼不是一個(gè)好習(xí)慣。的確,妥妥的Magic Number,但還有一個(gè)有趣的事實(shí)你需要了解:并不是所有實(shí)現(xiàn)都支持自旋鎖設(shè)置pshared。比如:

intpthread_spin_init(pthread_spinlock_t*lock,intpshared){
/*RelaxedMOisfinebecausethisisaninitializingstore.*/
atomic_store_relaxed(lock,0);
return0;
}

所以直接傳0可能也無(wú)傷大雅。

自旋鎖 VS 互斥量+條件變量 孰優(yōu)孰劣?肯定要看具體的使用場(chǎng)景,(我好像在說片湯話)。當(dāng)你不知道在你的使用場(chǎng)景下這兩種鎖該用哪個(gè)的時(shí)候,那就是用互斥量吧!

或者通過壓測(cè)的判斷,不過大多數(shù)時(shí)候我們好像并不需要這么一個(gè)pthread的自旋鎖,知友們可以提供一些自旋鎖的使用參考。

編輯:jq
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11080

    瀏覽量

    217155
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7256

    瀏覽量

    91923
  • Mac
    Mac
    +關(guān)注

    關(guān)注

    0

    文章

    1117

    瀏覽量

    53133
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4381

    瀏覽量

    64926

原文標(biāo)題:如何理解互斥鎖、條件變量、讀寫鎖以及自旋鎖?

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    什么是電磁控制板?24路控板的使用步驟概述

    電磁控制板是一種專用于控制電磁工作狀態(tài)的電子裝置,它可以接收指令并對(duì)電磁進(jìn)行通斷電操作,從而實(shí)現(xiàn)對(duì)相關(guān)設(shè)備的鎖定和解鎖控制。電磁本身依靠電流產(chǎn)生磁場(chǎng)作用力來吸附或釋放
    的頭像 發(fā)表于 07-14 16:26 ?157次閱讀
    什么是電磁<b class='flag-5'>鎖</b>控制板?24路<b class='flag-5'>鎖</b>控板的使用步驟概述

    存器的工作原理與作用

    存器(Latch)作為數(shù)字電路中的基本元件,扮演著存儲(chǔ)單個(gè)比特信息的關(guān)鍵角色。其獨(dú)特的功能和廣泛的應(yīng)用領(lǐng)域使其成為數(shù)字電路設(shè)計(jì)和實(shí)現(xiàn)的核心組成部分。本文將深入探討存器的基本概念、工作原理、類型、主要作用
    的頭像 發(fā)表于 02-03 16:53 ?2409次閱讀

    電路工作原理 自電路與常開電路的區(qū)別

    。 基本組成與結(jié)構(gòu) 自電路通常由電源開關(guān)、啟動(dòng)按鈕、停止按鈕、接觸器以及相關(guān)的輔助常開觸點(diǎn)組成。其中,接觸器是自電路中的關(guān)鍵元件,它包含一個(gè)電磁線圈和若干個(gè)常開、常閉觸點(diǎn)。 工作原理詳解 當(dāng)電機(jī)啟動(dòng)時(shí),首先合上
    的頭像 發(fā)表于 01-31 10:07 ?2946次閱讀

    電路在智能家居中的運(yùn)用

    一、自電路的基本原理 自電路,又稱為自保持電路,是一種能夠在電路中形成正反饋的電路結(jié)構(gòu)。當(dāng)電路中的某個(gè)條件滿足時(shí),電路能夠自動(dòng)保持當(dāng)前狀態(tài),無(wú)需外部持續(xù)的控制信號(hào)。這種電路通常由一個(gè)觸發(fā)器(如
    的頭像 發(fā)表于 01-18 10:17 ?896次閱讀

    電路與非自電路的比較

    ,使得電路在一定條件下能夠保持其狀態(tài)的電路。這種電路在接收到初始信號(hào)后,即使初始信號(hào)消失,電路也能維持其狀態(tài),直到接收到新的信號(hào)。 工作原理 自電路通常包含一個(gè)或多個(gè)反饋回路,這些回路能夠?qū)⑤敵鲂盘?hào)的一部分反饋到
    的頭像 發(fā)表于 01-18 10:11 ?826次閱讀

    電路模組選擇指南

    1. 自電路模組的基本原理 自電路模組的核心是一個(gè)繼電器,它包含一個(gè)控制線圈和一個(gè)或多個(gè)觸點(diǎn)。當(dāng)控制線圈通電時(shí),繼電器的觸點(diǎn)會(huì)閉合,即使控制線圈斷電,觸點(diǎn)也會(huì)保持閉合狀態(tài),這就是所謂的自功能
    的頭像 發(fā)表于 01-18 10:08 ?647次閱讀

    電路常見故障及解決方法

    電路因其便捷性和實(shí)用性,在工業(yè)和日常生活中得到了廣泛應(yīng)用。然而,由于各種因素,自電路可能會(huì)出現(xiàn)故障。 自電路原理 在討論故障之前,首先需要了解
    的頭像 發(fā)表于 01-18 10:05 ?2068次閱讀

    電路的類型和特點(diǎn)

    在電子工程領(lǐng)域,自電路是一種常見的設(shè)計(jì),它能夠使電路在沒有持續(xù)的觸發(fā)信號(hào)的情況下保持其狀態(tài)。這種電路的設(shè)計(jì)對(duì)于實(shí)現(xiàn)自動(dòng)化控制和減少人為干預(yù)至關(guān)重要。 一、自電路的類型 自電路可以根據(jù)其工作原理
    的頭像 發(fā)表于 01-18 10:03 ?936次閱讀

    存器的基本輸出時(shí)序

    深入探討存器的輸出時(shí)序時(shí),我們需要詳細(xì)分析存器在不同控制信號(hào)下的行為表現(xiàn),特別是控制信號(hào)(如使能信號(hào)E)的電平變化如何影響數(shù)據(jù)輸入(D)到輸出(Q)的傳輸過程。以下是對(duì)存器輸出
    的頭像 發(fā)表于 08-30 10:43 ?1182次閱讀

    sr存器約束條件怎樣得出的

    SR存器是一種常見的數(shù)字邏輯電路,它具有保持信號(hào)狀態(tài)的功能。在設(shè)計(jì)和分析SR存器時(shí),我們需要了解其約束條件。 一、引言 在數(shù)字邏輯電路設(shè)計(jì)中,
    的頭像 發(fā)表于 08-28 10:47 ?1704次閱讀

    rs存器不定狀態(tài)的含義是什么

    RS存器(Reset-Set Latch)中的不定狀態(tài),是指在特定輸入條件下,存器的輸出狀態(tài)變得不確定或不可預(yù)測(cè)的現(xiàn)象。這種不定狀態(tài)主要源于RS存器的輸入邏輯和電路特性,具體含義
    的頭像 發(fā)表于 08-28 10:42 ?1489次閱讀

    d存器解決了sr存器的什么問題

    D存器(Data Latch)和SR存器(Set-Reset Latch)是數(shù)字電路中常見的兩種存儲(chǔ)元件。它們?cè)跀?shù)字系統(tǒng)中扮演著重要的角色,用于存儲(chǔ)和傳遞信息。然而,這兩種存器在設(shè)計(jì)和應(yīng)用上
    的頭像 發(fā)表于 08-28 09:16 ?1187次閱讀

    rs存器和sr存器有什么區(qū)別嗎

    RS存器和SR存器是數(shù)字電路中兩種常見的存儲(chǔ)單元,它們?cè)诠δ芎蛻?yīng)用上有一些區(qū)別。 RS存器 RS存器,即Reset-Set存器,是
    的頭像 發(fā)表于 07-23 14:15 ?2347次閱讀

    兩種SR存器的約束條件

    基本約束條件: SR存器是一種基本的數(shù)字邏輯電路,用于存儲(chǔ)一位二進(jìn)制信息。它有兩個(gè)輸入端:S(Set)和R(Reset),以及兩個(gè)輸出端:Q和Q'(Q的反相)。以下是SR存器的基本
    的頭像 發(fā)表于 07-23 11:34 ?1818次閱讀

    存器電路中的中間是什么元件

    的主要作用是緩存數(shù)據(jù),解決高速控制器與慢速外設(shè)之間的不同步問題,以及解決驅(qū)動(dòng)和I/O口的輸入輸出問題。 類型 存器有多種類型,包括RS存器、D存器、JK
    的頭像 發(fā)表于 07-23 11:29 ?723次閱讀