在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

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

Linux愛好者 ? 來源:果凍蝦仁 ? 作者:果凍蝦仁 ? 2021-11-01 10:02 ? 次閱讀

鎖是一個常見的同步概念,我們都聽說過加鎖(lock)或者解鎖(unlock),當然學術一點的說法是獲取(acquire)和釋放(release)。

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

mutex(互斥量)

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

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

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

mutex是睡眠等待(sleep waiting)類型的鎖,當線程搶互斥鎖失敗的時候,線程會陷入休眠。優點就是節省CPU資源,缺點就是休眠喚醒會消耗一點時間。另外自從Linux 2.6版以后,mutex完全用futex的API實現了,內部系統調用的開銷大大減小。

值得一提的是,pthread的鎖一般都有一個trylock的函數,比如對于互斥量:

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

pthread_mutex_trylock用于以非阻塞的模式來請求互斥量。就好比各種IO函數都有一個noblock的模式一樣,對于加鎖這件事也有類似的非阻塞模式。

當線程嘗試加鎖時,如果鎖已經被其他線程鎖定,該線程就會阻塞住,直到能成功acquire。但有時候我們不希望這樣。

pthread_mutex_trylock在被其他線程鎖定時,會返回特殊錯誤碼。加鎖成返回0,僅當成功但時候,我們才能解鎖在后面進行解鎖操作!

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

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

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

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

//聲明一個互斥量
pthread_mutex_tmtx;
//聲明一個互斥量的屬性變量
pthread_mutexattr_tmtx_attr;

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

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

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

可重入鎖這個概念和稱呼的走俏多半是Java語言的功勞。

condition variable(條件變量)

請注意條件變量不是鎖,它是一種線程間的通訊機制,并且幾乎總是和互斥量一起使用的。所以互斥量和條件變量二者一般是成套出現的。比如C++11中也有條件變量的API:std::condition_variable

對于pthread:

//聲明一個互斥量
pthread_mutex_tmtx;
//聲明一個條件變量
pthread_cond_tcond;
...

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

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

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

pthread_cond_wait函數會把條件變量和互斥量都傳入。并且多線程調用的時候條件變量和互斥量一定要一一對應,不能一個條件變量在不同線程中wait的時候傳入不同的互斥量。否則是未定義結果。

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

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

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

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

比如半同步/半reactor網絡模型中,在工作線程消費fd隊列的時候:

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);//處理流程,業務邏輯
}

read-write lock(讀寫鎖)

顧名思義『讀寫鎖』就是對于臨界區區分讀和寫。在讀多寫少的場景下,不加區分的使用互斥量顯然是有點浪費的。此時便該上演讀寫鎖的拿手好戲。

讀寫鎖有一個別稱叫『共享-獨占鎖』。不過單看『共享-獨占鎖』或者『讀寫鎖』這兩個名稱,其實并未區分對于讀和寫,到底誰共享,誰獨占。可能會讓人誤以為讀寫鎖是一種更為泛化的稱呼,其實不是。讀寫鎖的含義是準確的:是一種 讀共享,寫獨占的鎖。

讀寫鎖的特性:

  • 當讀寫鎖被加了寫鎖時,其他線程對該鎖加讀鎖或者寫鎖都會阻塞(不是失敗)。
  • 當讀寫鎖被加了讀鎖時,其他線程對該鎖加寫鎖會阻塞,加讀鎖會成功。

因而適用于多讀少寫的場景。

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

...共享資源的讀操作

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

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

...共享資源的寫操作

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

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

其實加讀鎖和加寫鎖這兩個說法可能會造成誤導,讓人誤以為是有兩把鎖,其實讀寫鎖是一個鎖。所謂加讀鎖和加寫鎖,準確的說法可能是『給讀寫鎖加讀模式的鎖定和加寫模式的鎖定』。

讀寫鎖和互斥量一樣也有trylock函數,也是以非阻塞地形式來請求鎖,不會導致阻塞。

pthread_rwlock_tryrdlock(&rwlock)
pthread_rwlock_trywrlock(&rwlock)

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

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

另外多讀少寫的場景有些特殊場景,可以用特殊的數據結構減少鎖使用:

  • 多讀單寫的線性數據。用數組實現環形隊列,避免vector等動態擴張的數據結構,寫在結尾,由于單寫因而可以不加鎖;讀在開頭,由于多讀(避免重復消費)所以需要加一下鎖(互斥量就行)。
  • 多讀單寫的KV。可以使用雙緩沖(double buffer)的數據結構來實現。double buffer同名的概念比較多,這里指的是foreground 和 backgroud 兩個buffer進行切換的『0 - 1切換』技術。比如實現動態加載(熱加載)配置文件的時候。可能會在切換間隙加一個短暫的互斥量,但是基本可以認為是lock free的。

我一張口,你就會發現:無非是空間換時間的老套路了。

spinlock(自旋鎖)

自旋之名頗為玄妙,第一次聽聞常讓人略覺高大。但和無數個好似『故意把簡單概念復雜化』的計算機術語一樣,自旋鎖的本質簡單的難以置信。

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

單看使用方法和使用互斥量的代碼是差不多的。只不過自旋鎖不會引起線程休眠。當共享資源的狀態不滿足的時候,自旋鎖會不停地循環檢測狀態。因為不會陷入休眠,而是忙等待的方式也就不需要條件變量。

這是優點也是缺點。不休眠就不會引起上下文切換,但是會比較浪費CPU。

//聲明一個自旋鎖變量
pthread_spinlock_tspinlock;

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

//加鎖
pthread_spin_lock(&spinlock);

//解鎖
pthread_spin_unlock(&spinlock);

//銷毀
pthread_spin_destroy(&spinlock);

pthread_spin_init函數的第二個參數名為pshared(int類型)。表示的是是否能進程間共享自旋鎖。這被稱之為Thread Process-Shared Synchronization。互斥量的通過屬性也可以把互斥量設置成進程間共享的。pshared有兩個枚舉值:

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

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

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

所以直接傳0可能也無傷大雅。

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

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

編輯:jq
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • cpu
    cpu
    +關注

    關注

    68

    文章

    10868

    瀏覽量

    211844
  • 數據
    +關注

    關注

    8

    文章

    7045

    瀏覽量

    89062
  • Mac
    Mac
    +關注

    關注

    0

    文章

    1106

    瀏覽量

    51485
  • 函數
    +關注

    關注

    3

    文章

    4332

    瀏覽量

    62641

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

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    存器的基本輸出時序

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

    SR存器的功能有哪些?

    信號滿足一定的條件時,SR存器可以將輸入信號存儲在內部,并通過輸出端將存儲的信息傳遞給其他電路。 保持功能 SR存器具有保持功能,即在沒有輸入信號的情況下,它可以保持內部存儲的狀態不變。這種特性使得SR
    的頭像 發表于 08-28 10:55 ?931次閱讀

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

    SR存器是一種常見的數字邏輯電路,它具有保持信號狀態的功能。在設計和分析SR存器時,我們需要了解其約束條件。 一、引言 在數字邏輯電路設計中,
    的頭像 發表于 08-28 10:47 ?654次閱讀

    rs存器不定狀態的含義是什么

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

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

    D存器(Data Latch)和SR存器(Set-Reset Latch)是數字電路中常見的兩種存儲元件。它們在數字系統中扮演著重要的角色,用于存儲和傳遞信息。然而,這兩種存器在設計和應用上
    的頭像 發表于 08-28 09:16 ?578次閱讀

    rs存器和sr存器有什么區別嗎

    RS存器和SR存器是數字電路中兩種常見的存儲單元,它們在功能和應用上有一些區別。 RS存器 RS存器,即Reset-Set存器,是
    的頭像 發表于 07-23 14:15 ?1201次閱讀

    兩種SR存器的約束條件

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

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

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

    存器原態和新態的定義

    存器(Latch)是一種存儲單元,用于存儲一位二進制信息。在數字電路中,存器是一種基本的存儲元件,廣泛應用于寄存器、計數器、觸發器等電路中。存器的原態和新態是描述存器狀態變化的
    的頭像 發表于 07-23 10:21 ?541次閱讀

    互斥自旋的實現原理

    互斥自旋是操作系統中常用的同步機制,用于控制對共享資源的訪問,以避免多個線程或進程同時訪問同一資源,從而引發數據不一致或競爭條件等問題
    的頭像 發表于 07-10 10:07 ?498次閱讀

    自旋互斥的使用場景是什么

    自旋互斥是兩種常見的同步機制,它們在多線程編程中被廣泛使用。在本文中,我們將介紹自旋
    的頭像 發表于 07-10 10:05 ?1011次閱讀

    RTThread4.1.1在spiflash上掛dfs文件系統報互斥錯誤的原因?

    最近使用gd32f450vg芯片,在SPI4接口上掛了gd25q32,想使用dfs文件系統,gd25q32能夠正常的識別,顯示文件系統掛載正常,但是只要操作文件系統就會出現報錯,看像是互斥的問題,請問這個要從哪個方向查原因
    發表于 03-05 07:39

    交流接觸器的自和互鎖

    和互鎖是交流接觸器中兩個重要的概念,它們分別指的是接觸器在特定情況下的工作狀態。在本文中,我將詳細討論自和互鎖的定義、原理、應用和相關特點。 首先,讓我們先了解和互鎖的基本概
    的頭像 發表于 01-16 10:23 ?3026次閱讀

    U-tec宣布推出首款帶指紋讀取器的閂智能

    智能制造商U-tec宣布推出首款帶指紋讀取器的閂智能,支持Matter-over-Thread。
    的頭像 發表于 01-12 16:17 ?1011次閱讀

    信號量實現原理介紹

    除了原子操作,中斷屏蔽,自旋以及自旋的衍生之外,在Linux內核中還存在著一些其他同步
    的頭像 發表于 01-10 09:07 ?1201次閱讀
    主站蜘蛛池模板: 欧洲色妇| 视频免费观看网址| 色人人| 国产亚洲高清视频| 99久精品| 狠狠欧美| 欧美潘金莲一级风流片a级| 亚洲人成在线精品| 欧美日本一区二区| 欧美性幼| 亚洲人成在线精品不卡网| 天天操2023| 夜夜综合网| 97影院理论片在线观看| 午夜免费观看福利片一区二区三区| 国产高清免费在线| 精品国产综合区久久久久99| 欧美精品国产第一区二区| 久久综合一| 久久大伊人| 久久ww| 夜夜夜网| 插插插天天| 五月sese| 免费视频不卡| 日本亚洲在线| 性做久久久久久| 日本一区二区三区不卡在线视频| 国产超爽人人爽人人做| 国产三级久久久精品三级| 婷婷欧美| 天天看片天天操| 天堂综合| 毛片网子| 国产女人视频| 俺去久久| 闲人综合| 天堂网站| 操女人网址| 亚洲国产精品第一页| 男人j桶进女人免费视频|