欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go并發(fā)編程之死鎖與活鎖的案例分析

 更新時間:2023年04月30日 08:47:27   作者:不背鍋運(yùn)維  
死鎖就是在并發(fā)程序中,兩個或多個線程彼此等待對方完成操作,從而導(dǎo)致它們都被阻塞,并無限期地等待對方完成;活鎖就是程序一直在運(yùn)行,但是無法取得進(jìn)展。本文將從一些案例出發(fā),分析一下它們,希望對大家有所幫助

什么是死鎖、活鎖

什么是死鎖:就是在并發(fā)程序中,兩個或多個線程彼此等待對方完成操作,從而導(dǎo)致它們都被阻塞,并無限期地等待對方完成。這種情況下,程序會卡死,無法繼續(xù)執(zhí)行。

什么是活鎖:就是程序一直在運(yùn)行,但是無法取得進(jìn)展。例如,在某些情況下,多個線程會爭奪同一個資源,然后每個線程都會釋放資源,以便其他線程可以使用它。但是,如果沒有正確的同步,這些線程可能會同時嘗試獲取該資源,然后再次釋放它。這可能導(dǎo)致線程在無限循環(huán)中運(yùn)行,卻無法取得進(jìn)展。

發(fā)生死鎖的案例分析

1.編寫會發(fā)生死鎖的代碼:

package?main

import?(
?"fmt"
?"sync"
)

func?main()?{
?var?mu?sync.Mutex
?mu.Lock()
?defer?mu.Unlock()

?wg?:=?sync.WaitGroup{}
?wg.Add(1)
?go?func()?{
??fmt.Println("goroutine?started")
??mu.Lock()?//?在這里獲取了鎖
??fmt.Println("goroutine?finished")
??mu.Unlock()
??wg.Done()
?}()

?wg.Wait()
}

運(yùn)行和輸出:

[root@workhost temp02]# go run main.go 
goroutine started
fatal error: all goroutines are asleep - deadlock! # 錯誤很明顯了,告訴你死鎖啦!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc000010030?)
        /usr/local/go/src/runtime/sema.go:62 +0x27
...
...

上面的代碼,使用 sync.Mutex 實現(xiàn)了一個互斥鎖。主 goroutine 獲取了鎖,并啟動了一個新的 goroutine。新 goroutine 也嘗試獲取鎖來執(zhí)行其任務(wù)。但是,由于主 goroutine 沒有釋放鎖,新 goroutine 將一直等待鎖,導(dǎo)致死鎖。

2.代碼改造

在上面的代碼中,可以通過將主 goroutine 中的 defer mu.Unlock() 移到 goroutine 函數(shù)中的 mu.Unlock() 后面來解決問題。這樣,當(dāng) goroutine 獲取到鎖后,它可以在完成任務(wù)后釋放鎖,以便主 goroutine 可以繼續(xù)執(zhí)行。

改造后的代碼:

package?main

import?(
?"fmt"
?"sync"
)

func?main()?{
?var?mu?sync.Mutex
?mu.Lock()
?wg?:=?sync.WaitGroup{}
?wg.Add(1)
?go?func()?{
??fmt.Println("goroutine?started")
??mu.Lock()?//?在這里獲取了鎖
??fmt.Println("goroutine?finished")
??mu.Unlock()
??wg.Done()
?}()
?mu.Unlock()?//?釋放鎖
?wg.Wait()
}

運(yùn)行和輸出:

[root@workhost temp02]# go run main.go 
goroutine started
goroutine finished

3.如何避免死鎖

在 Go 語言中,要避免死鎖,一定要清楚以下幾個規(guī)則:

  • 避免嵌套鎖:在使用多個鎖時,確保它們的嵌套順序相同。否則,可能會出現(xiàn)循環(huán)等待的情況,導(dǎo)致死鎖。
  • 避免無限等待:如果在獲取鎖時指定了超時時間,確保在超時后能夠處理錯誤或執(zhí)行其他操作。
  • 避免過度競爭:如果多個協(xié)程需要訪問相同的資源,請確保它們不會互相干擾。可以使用互斥鎖或讀寫鎖等機(jī)制來解決競爭問題。
  • 使用通道:Go 語言中的通道可以用于協(xié)調(diào)并發(fā)操作。使用通道來傳遞消息和同步操作,可以避免死鎖和競爭問題。
  • 確保資源釋放:在使用鎖或其他資源時,一定要確保它們在使用后得到釋放,否則可能會導(dǎo)致死鎖。
  • 使用 select 語句:在使用通道進(jìn)行并發(fā)操作時,可以使用 select 語句來避免死鎖。通過 select 語句選擇多個通道中的一個進(jìn)行操作,可以避免在某個通道被阻塞時出現(xiàn)死鎖。

發(fā)生活鎖的案例分析

1.編寫會發(fā)生活鎖的代碼:

package?main

import?(
?"fmt"
?"sync"
)

func?main()?{
?var?wg?sync.WaitGroup
?var?mu?sync.Mutex
?var?flag?bool

?wg.Add(2)

?//?goroutine?1
?go?func()?{
??//?先獲取鎖資源
??fmt.Println("goroutine?1?獲取?mu")
??mu.Lock()
??defer?mu.Unlock()

??//?然后等待?flag?變量的值變?yōu)?true
??fmt.Println("goroutine?1?等待標(biāo)志")
??for?!flag?{
???//?不斷循環(huán)等待
??}

??//?最終輸出并釋放鎖資源
??fmt.Println("goroutine?1?從等待中釋放")
??wg.Done()
?}()

?//?goroutine?2
?go?func()?{
??//?先獲取鎖資源
??fmt.Println("goroutine?2?獲取?mu")
??mu.Lock()
??defer?mu.Unlock()

??//?然后等待?flag?變量的值變?yōu)?true
??fmt.Println("GoRoutine2?等待標(biāo)志")
??for?!flag?{
???//?不斷循環(huán)等待
??}

??//?最終輸出并釋放鎖資源
??fmt.Println("GoRoutine?2?從等待中釋放")
??wg.Done()
?}()

?//?在主線程中等待?1?秒鐘,以便兩個?goroutine?開始等待?flag?變量的值
?//?然后將?flag?變量設(shè)置為?true
?//?由于兩個?goroutine?會同時喚醒并嘗試獲取鎖資源,它們會相互等待
?//?最終導(dǎo)致了活鎖問題,它們都無法向前推進(jìn)
?fmt.Println("主線程休眠?1?秒")
?fmt.Println("兩個goroutine都應(yīng)該等待標(biāo)志")
?flag?=?true
?wg.Wait()

?fmt.Println("所有?GoRoutines?已完成")
}

運(yùn)行和輸出:

[root@workhost temp02]# go run main.go 
主線程休眠 1 秒
兩個goroutine都應(yīng)該等待標(biāo)志
goroutine 2 獲取 mu
GoRoutine2 等待標(biāo)志
GoRoutine 2 從等待中釋放
goroutine 1 獲取 mu
goroutine 1 等待標(biāo)志
goroutine 1 從等待中釋放
所有 GoRoutines 已完成

上面的代碼存在活鎖問題。如果兩個goroutine同時等待flag變?yōu)閠rue并且都已經(jīng)獲取了鎖資源,那么它們就會進(jìn)入一個死循環(huán)并相互等待,無法繼續(xù)向前推進(jìn)。

2.代碼改造

改造后的代碼:

package?main

import?(
?"fmt"
?"runtime"
?"sync"
)

func?main()?{
?var?wg?sync.WaitGroup
?var?mu?sync.Mutex
?var?flag?bool

?wg.Add(2)

?//?goroutine?1
?go?func()?{
??//?先獲取鎖資源
??fmt.Println("goroutine?1?獲取?mu")
??mu.Lock()
??defer?mu.Unlock()

??//?然后等待?flag?變量的值變?yōu)?true
??fmt.Println("goroutine?1?等待標(biāo)志")
??for?!flag?{
???runtime.Gosched()?//?讓出時間片
??}

??//?最終輸出并釋放鎖資源
??fmt.Println("goroutine?1?從等待中釋放")
??wg.Done()
?}()

?//?goroutine?2
?go?func()?{
??//?先獲取鎖資源
??fmt.Println("goroutine?2?獲取?mu")
??mu.Lock()
??defer?mu.Unlock()

??//?然后等待?flag?變量的值變?yōu)?true
??fmt.Println("GoRoutine2?等待標(biāo)志")
??for?!flag?{
???runtime.Gosched()?//?讓出時間片
??}

??//?最終輸出并釋放鎖資源
??fmt.Println("GoRoutine?2?從等待中釋放")
??wg.Done()
?}()

?//?在主線程中等待?1?秒鐘,以便兩個?goroutine?開始等待?flag?變量的值
?//?然后將?flag?變量設(shè)置為?true
?//?由于兩個?goroutine?會同時喚醒并嘗試獲取鎖資源,它們會相互等待
?//?最終導(dǎo)致了活鎖問題,它們都無法向前推進(jìn)
?fmt.Println("主線程休眠?1?秒")
?fmt.Println("兩個goroutine都應(yīng)該等待標(biāo)志")
?flag?=?true
?wg.Wait()

?fmt.Println("所有?GoRoutines?已完成")
}

改造后的代碼在等待flag變量的循環(huán)中加入了讓出時間片的函數(shù) runtime.Gosched(),這樣兩個goroutine在等待期間可以放棄時間片,以便其他goroutine可以執(zhí)行并獲得鎖資源。這種方式可以有效地減少競爭程度,從而避免了活鎖問題。

3.如何避免發(fā)生活鎖的可能性

在 Go 語言的并發(fā)編程中,避免活鎖的關(guān)鍵是正確地實現(xiàn)同步機(jī)制。以下是一些避免活鎖的方法:

  • 避免忙等待:使用 sync.Cond 或者 channel 等同步機(jī)制來實現(xiàn)等待。這樣避免了線程一直占用 CPU 資源而無法取得進(jìn)展的問題。
  • 避免死鎖:死鎖往往是活鎖的前提,因此正確地使用鎖和同步機(jī)制可以避免死鎖,從而避免活鎖。
  • 減少鎖的粒度:盡可能將鎖的粒度縮小到最小范圍,避免鎖住不必要的代碼塊。
  • 采用超時機(jī)制:使用 sync.Mutex 的 TryLock() 方法或者使用 select 語句實現(xiàn)等待超時機(jī)制,這樣可以防止線程無限期等待。
  • 合理設(shè)計并發(fā)模型:合理設(shè)計并發(fā)模型可以避免競爭和饑餓等問題,進(jìn)而避免活鎖的發(fā)生。

以上就是Go并發(fā)編程之死鎖與活鎖的案例分析的詳細(xì)內(nèi)容,更多關(guān)于Go死鎖 活鎖的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go?zero微服務(wù)實戰(zhàn)性能優(yōu)化極致秒殺

    go?zero微服務(wù)實戰(zhàn)性能優(yōu)化極致秒殺

    這篇文章主要為大家介紹了go-zero微服務(wù)實戰(zhàn)性能優(yōu)化極致秒殺功能實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Go?實戰(zhàn)單隊列到優(yōu)先級隊列實現(xiàn)圖文示例

    Go?實戰(zhàn)單隊列到優(yōu)先級隊列實現(xiàn)圖文示例

    這篇文章主要為大家介紹了Go?實戰(zhàn)單隊列到優(yōu)先級隊列圖文示例實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 使用go來操作redis的方法示例

    使用go來操作redis的方法示例

    今天小編就為大家分享一篇關(guān)于使用go來操作redis的方法示例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Golang自動追蹤GitHub上熱門AI項目

    Golang自動追蹤GitHub上熱門AI項目

    這篇文章主要為大家介紹了Golang自動追蹤GitHub上熱門AI項目,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • go語言版的ip2long函數(shù)實例

    go語言版的ip2long函數(shù)實例

    這篇文章主要介紹了go語言版的ip2long函數(shù),實例分析了Go語言實現(xiàn)的ip2long函數(shù)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • Golang并發(fā)操作中常見的讀寫鎖詳析

    Golang并發(fā)操作中常見的讀寫鎖詳析

    Golang中的鎖機(jī)制主要包含互斥鎖和讀寫鎖互斥鎖互斥鎖是傳統(tǒng)并發(fā)程序?qū)蚕碣Y源進(jìn)行控制訪問的主要手段,這篇文章主要給大家介紹了關(guān)于Golang并發(fā)操作中常見的讀寫鎖的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • golang 比較浮點(diǎn)數(shù)的大小方式

    golang 比較浮點(diǎn)數(shù)的大小方式

    這篇文章主要介紹了golang 比較浮點(diǎn)數(shù)的大小方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • golang時間字符串和時間戳轉(zhuǎn)換的案例

    golang時間字符串和時間戳轉(zhuǎn)換的案例

    這篇文章主要介紹了golang時間字符串和時間戳轉(zhuǎn)換的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言基礎(chǔ)學(xué)習(xí)之Context的使用詳解

    Go語言基礎(chǔ)學(xué)習(xí)之Context的使用詳解

    在Go語言中,Context是一個非常重要的概念,它用于在不同的?goroutine?之間傳遞請求域的相關(guān)數(shù)據(jù),本文將深入探討Go語言中?Context特性和Context的高級使用方法,希望對大家有所幫助
    2023-05-05
  • go mod 使用私有g(shù)itlab群組的解決方案

    go mod 使用私有g(shù)itlab群組的解決方案

    這篇文章主要介紹了go mod 使用私有g(shù)itlab群組的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05

最新評論