兩個線程,兩個互斥鎖,怎麼形成一個死循環?

2021-01-07 酷扯兒

本文轉載自【微信公眾號:strongerHuang,ID:strongerHuang】經微信公眾號授權轉載,如需轉載與原文作者聯繫

兩個線程,兩個互斥鎖如何形成死鎖?

程序流程圖如下:

程序流程圖

如上圖所示:

t0時刻,主線程創建子線程,並初始化互斥鎖mutex1、mutex2;t1時刻,主線程申請到了mutex1、子線程申請到了mutex2;t2時刻,主線程和子線程都sleep 1秒鐘,防止優先獲得時間片的線程直接申請到了另外1個互斥鎖,導致程序直接退出;t3時刻,主線程和子線程都想獲得對方手裡的互斥鎖,但是對方都來不及釋放自己手裡的鎖;t4時刻,主線程和子線雙雙進入休眠。【注意】為了保證主線程和子線程都能夠分別獲得鎖mutex1、mutex2,各自獲得鎖後一定要先sleep 1秒鐘,否則創建完子線程後,主線程還有一定的時間片,主線程會申請到鎖mutex2,無法形成死鎖。

死鎖

源碼如下

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

unsigned int value1, value2, count;

pthread_mutex_t mutex1,mutex2;

void *function(void *arg);

void *function(void *arg)

{

pthread_mutex_lock(&mutex2);

printf("new thread get mutex2\n");

sleep(1);

pthread_mutex_lock(&mutex1);

printf("new thread get mutex1\n");

pthread_mutex_unlock(&mutex1);

printf("new thread release mutex1\n");

pthread_mutex_unlock(&mutex2);

printf("new thread release mutex2\n");

return NULL;

}

int main(int argc, char *argv[])

{

pthread_t a_thread;

if (pthread_mutex_init(&mutex1, NULL) < 0)

{

perror("fail to mutex_init");

exit(-1);

}

if (pthread_mutex_init(&mutex2, NULL) < 0)

{

perror("fail to mutex_init");

exit(-1);

}

if (pthread_create(&a_thread, NULL, function, NULL) < 0)

{

perror("fail to pthread_create");

exit(-1);

}

while ( 1 )

{

pthread_mutex_lock(&mutex1);

printf("main thread get mutex1\n");

sleep(1);

pthread_mutex_lock(&mutex2);

printf("main thread get mutex2\n");

pthread_mutex_unlock(&mutex2);

printf("main thread release mutex2\n");

pthread_mutex_unlock(&mutex1);

printf("main thread release mutex1\n");

}

return 0;

}

編譯運行

從執行結果可以判斷,主線程和子線程分別獲得了互斥鎖mutex1、mutex2,sleep1秒後,他們都想再分別申請mutex2、mutex1,而雙方都不想釋放自己手中的鎖,鎖已形成了死鎖,程序就一直處於休眠狀態。

查看下該進程的線程

查看進程ID,為4204

查看該進程創建的線程id:4204、4205。

相關焦點

  • 面試官:你說說互斥鎖、自旋鎖、讀寫鎖、悲觀鎖、樂觀鎖的應用場景
    當已經有一個線程加鎖後,其他線程加鎖則就會失敗,互斥鎖和自旋鎖對於加鎖失敗後的處理方式是不一樣的:互斥鎖加鎖失敗後,線程會釋放 CPU ,給其他線程;自旋鎖加鎖失敗後,線程會忙等待,一般加鎖的過程,包含兩個步驟:第一步,查看鎖的狀態,如果鎖是空閒的,則執行第二步;第二步,將鎖設置為當前線程持有;CAS 函數就把這兩個步驟合併成一條硬體級指令,形成原子指令,這樣就保證了這兩個步驟是不可分割的,要麼一次性執行完兩個步驟,要麼兩個步驟都不執行。
  • Linux Qt使用POSIX多線程條件變量、互斥鎖(量)
    比如說需要對線程間共享的數據提供保護,使用互斥量同步、使用條件變量、使用讀寫鎖同步等;各種同步方式用在什麼情況下,開始編程時多線程使用的並不多,無法切身體會到這些問題,後來程序寫的多了一點兒,慢慢接觸到一些多線程的東西,並且自己也可以學習了相關知識,並用到實際程序中。好了,下面以一個實際的例子為背景,來說明Linux POSIX多線程的一些特性。
  • 如何理解互斥鎖、條件變量、讀寫鎖以及自旋鎖?
    其思想簡單粗暴,多線程共享一個互斥量,然後線程之間去競爭。得到鎖的線程可以進入臨界區執行代碼。當線程搶互斥鎖失敗的時候,線程會陷入休眠。在讀多寫少的場景下,不加區分的使用互斥量顯然是有點浪費的。此時便該上演讀寫鎖的拿手好戲。讀寫鎖有一個別稱叫『共享-獨佔鎖』。不過單看『共享-獨佔鎖』或者『讀寫鎖』這兩個名稱,其實並未區分對於讀和寫,到底誰共享,誰獨佔。可能會讓人誤以為讀寫鎖是一種更為泛化的稱呼,其實不是。讀寫鎖的含義是準確的:是一種 讀共享,寫獨佔的鎖。
  • Linux 多線程詳解 —— 線程安全
    多個線程訪問同一塊臨界資源,導致資源產生二義性的現象。舉一個例子假設現在有兩個線程A和B,單核CPU的情況下,此時有一個int類型的全局變量為100,A和B的入口函數都要對這個全局變量進行–操作。線程A先拿到CPU資源後,對全局變量進行–操作並不是原子性操作,也就是意味著,A在執行–的過程中有可能會被打斷。
  • C語言描述線程間的同步與互斥 - 計算機java編程
    一、實現線程間同步互斥的操作1、線程間同步 ==== 有序執行法1、多個信號量  法2、條件變量+互斥鎖 ===>broadcast signal2、線程間互斥 ==== "你死我活"法1、單個信號量  法2、互斥鎖//1、互斥是指某一資源同時只允許一個訪問者對其進行訪問
  • C++11線程、鎖和條件變量
    ,頭一個方法用來對互斥量進行加鎖,如果互斥量不可得便會處於阻塞狀態;第二個方法用來對互斥量進行解鎖。,本標準還提供了幾個用來對一個或多個互斥量進行加鎖的方法。這裡舉一個造成死鎖的例子:我們有一個保存元素的容器,還有一個叫做exchange()的方法,用來將一個元素從一個容器中取出來放入另外一個容器。為了成為線程安全的函數,這個函數通過獲得每個容器的互斥量,對兩個容器的訪問進行了同步處理。
  • Java 中15種鎖的介紹:公平鎖,可重入鎖,獨享鎖,互斥鎖,樂觀鎖,分段鎖,自旋鎖等等
    在這種方式下,只有一個線程能夠訪問被互斥鎖保護的資源讀寫鎖讀寫鎖既是互斥鎖,又是共享鎖,read模式是共享,write是互斥(排它鎖)的。自旋鎖(spinlock):是指當一個線程在獲取鎖的時候,如果鎖已經被其它線程獲取,那麼該線程將循環等待,然後不斷的判斷鎖是否能夠被成功獲取,直到獲取到鎖才會退出循環。它是為實現保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為了解決對某項資源的互斥使用。
  • C 多線程的互斥鎖應用RAII機制
    RAII的做法是使用一個類對象,在對象的構造函數中獲取資源,在對象生命期內控制對資源的訪問,最後在對象消失時,其析構函數來釋放獲取的資源;這裡的資源可以是文件句柄,內存,Event,互斥量等等,由於系統的資源是有限的,就好比自然界的石油,鐵礦一樣,不是取之不盡,用之不竭的。所以,我們在編程安全上,要求必須遵循以下幾個步驟:1. 申請資源2.
  • 原子操作和互斥鎖的區別
    今天的文章裡我們會簡單了解一下Go語言裡對原子操作的支持,然後探討一下原子操作和互斥鎖的區別。文章的主要話題如下:原子操作Go對原子操作的支持原子操作和互斥鎖的區別原子操作原子操作即是進行過程中不能被中斷的操作,針對某個值的原子操作在被進行的過程中,CPU絕不會再去進行其他的針對該值的操作。
  • 預測讀寫鎖的死鎖問題,居然有人做到了?
    並且,一個潛在死鎖能且只能最早在最後一個加鎖順序(或鎖依賴)即將生成死鎖循環的時候被預測出來。基於引理1,解決死鎖預測問題就是在最後一個拿鎖順序(即鎖依賴)形成等待圓環(循環)時,通過某種方法計算出這個等待圓環是否構成潛在死鎖,而我們的任務就是找到這個方法(算法)。▍引理2:兩個虛擬線程 T1 和 T2 可以用來表示所有的死鎖場景。
  • Java多線程synchronized
    如何解決線程安全問題?那麼一般來說,是如何解決線程安全問題的呢?基本上所有的併發模式在解決線程安全問題時,都採用「序列化訪問臨界資源」的方案,即在同一時刻,只能有一個線程訪問臨界資源,也稱作同步互斥訪問。通常來說,是在訪問臨界資源的代碼前面加上一個鎖,當訪問完臨界資源後釋放鎖,讓其他線程繼續訪問。
  • Linux筆記(19)| 線程基礎(三)
    線程的同步是一個很重要的內容,因為這關係到線程之間的協調合作,否則可能會產生衝突。互斥鎖是一個簡單的鎖定命令,它可以用來鎖定對共享資源的訪問,對於線程來說,整個地址空間都是共享的資源,所以線程的任何資源都是共享的。
  • Java多線程之死鎖問題
    Java多線程之死鎖定義多個線程各自佔有一些共享的資源,並且互相等待,直到獲取到對方線程佔有的資源本身才能夠繼續運行,而導致了兩個或者兩個以上的線程同時在無休止地等待對方釋放資源,從而這些線程都處於停止狀態。某一個同步塊同時擁有兩個以上對象的鎖時,就可能發生死鎖的情況。
  • 熬了兩個通宵寫的!終於把多線程和多進程徹底講明白了!
    然後我們通過 Thead類新建了兩個線程,target參數就是剛才我們所定義的方法名,args以列表的形式傳遞。兩次循環中,這裡 i 分別就是 1 和 5,這樣兩個線程就分別休眠 1 秒和 5 秒,聲明完成之後,我們調用 start 方法即可開始線程的運行。
  • 「轉載」java架構之路(多線程)synchronized詳解以及鎖的膨脹升級...
    從無鎖到重量級鎖的一個升級過程,我們來邊畫圖,邊詳細看一下。的內存地址,並且線程A的owner也會存一份鎖對象的內存地址,形成一個雙向指向的形式。而線程B修改失敗,則進入一個自旋狀態,就是持續來修改鎖對象。
  • 共享鎖、排他鎖、互斥鎖、悲觀鎖、樂觀鎖、行鎖、表鎖、頁面鎖、不可重複讀、丟失修改、讀髒數據
    這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A互斥鎖: 在編程中,引入了對象互斥鎖的概念,來保證共享數據操作的完整性。每個對象都對應於一個可稱為" 互斥鎖" 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。
  • 24張圖帶你徹底理解Java中的21種鎖
    ,我們只須讓線程執行一個忙循環(自旋)。,能讓兩個或以上的線程同時並行執行,就可以讓後面請求鎖的那個線程「稍等一會」,但不放棄處理器的執行時間,看看持有鎖的線程是否很快就會釋放鎖。輕量級是相對於使用作業系統互斥量來實現的重量級鎖而言的。輕量級鎖在沒有多線程競爭的前提下,減少傳統的重量級鎖使用作業系統互斥量產生的性能消耗。如果出現兩條以上的線程爭用同一個鎖的情況,那輕量級鎖將不會有效,必須膨脹為重量級鎖。
  • (5)嵌入式QT多線程的簡單實現(方法一)
    那麼,調用對象qthread裡面的非run()函數,這些函數就會在ui線程中執行,並不會產生新的線程ID。因此,如果要執行耗時的任務,最好把任務邏輯都寫在run()函數中,否則,耗時的任務會把ui阻塞,導致ui出現卡死現象。還有一點要注意,如果要在非run()函數和run()函數裡面,進行qthread對象裡面的某一個變量修改,要注意進行加鎖操作。
  • Java多線程工具類之循環柵欄計數器
    來看看JDKAPI文檔中是怎麼介紹這個對象的:翻譯後大概意思:允許一組線程全部等待彼此達到共同的屏障點的同步輔助。循環阻塞在涉及固定大小的線程方的程序中很有用,這些線程必須偶爾等待彼此。屏障被稱為循環 ,因為它可以在等待的線程被釋放之後重新使用。
  • 開發中要注意如何避免死鎖、活鎖問題
    談到死鎖,相信大家都不陌生,耳熟能詳了吧,這個死鎖無論是在學習中、還是面試中都是必說的一個問題。小編先佔用大家時間說下什麼是死鎖?死鎖是指兩個或兩個以上的線程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。