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

Go語(yǔ)言單線程運(yùn)行也會(huì)有的并發(fā)問(wèn)題解析

 更新時(shí)間:2023年12月20日 09:22:41   作者:晁岳攀(鳥(niǎo)窩)?鳥(niǎo)窩聊技術(shù)  
這篇文章主要為大家介紹了Go語(yǔ)言單線程運(yùn)行的并發(fā)問(wèn)題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Go單線程多goroutine訪問(wèn)一個(gè)map會(huì)遇到并發(fā)讀寫panic么?

一個(gè) Go 大佬群中嚴(yán)肅的討論了一個(gè)問(wèn)題:Go 程序單線程多 goroutine 訪問(wèn)一個(gè) map 會(huì)遇到并發(fā)讀寫 panic 么?

答案是肯定的,因?yàn)槌霈F(xiàn)了這個(gè)問(wèn)題所以大家才在群中討論。

為什么呢?因?yàn)閱尉€程意味著并行單元只有一個(gè)(多線程也可能并行單元只有一個(gè)),但是多 goroutine 意味著并發(fā)單元有多個(gè),如果并發(fā)單元同時(shí)執(zhí)行,即使是單線程,可能就會(huì)產(chǎn)生數(shù)據(jù)競(jìng)爭(zhēng)的問(wèn)題,除非這些 goroutine 是順序執(zhí)行的。

舉一個(gè)例子哈:

func TestCounter() {
    runtime.GOMAXPROCS(1)
 var counter int
 var wg sync.WaitGroup
 wg.Add(10)
 for i := 0; i < 10; i++ {
  i := i
  go func() {
   fmt.Printf("start task#%d, counter: %d\n", i, counter)
   for j := 0; j < 10_0000; j++ {
    counter++
   }
   fmt.Printf("end task#%d, counter: %d\n", i, counter)
   wg.Done()
  }()
 }
 wg.Wait()
 fmt.Println(counter)
}

這段測(cè)試代碼是啟動(dòng) 10 個(gè) goroutine 對(duì)計(jì)數(shù)器加一,每個(gè) goroutine 負(fù)責(zé)加 10 萬(wàn)次。在我的 MBP m1 筆記本上,每次的結(jié)果都是 100 萬(wàn),符合期望。如果你運(yùn)行這段代碼,會(huì)發(fā)現(xiàn) goroutine 其實(shí)是一個(gè)一個(gè)串行執(zhí)行的(9->0->1->2->3->4->5->6->7->8,當(dāng)然可能在你的機(jī)器上不是這樣的),如果是串行執(zhí)行,不會(huì)有并發(fā)問(wèn)題:

start task#9, counter: 0
end task#9, counter: 100000
start task#0, counter: 100000
end task#0, counter: 200000
start task#1, counter: 200000
end task#1, counter: 300000
start task#2, counter: 300000
end task#2, counter: 400000
start task#3, counter: 400000
end task#3, counter: 500000
start task#4, counter: 500000
end task#4, counter: 600000
start task#5, counter: 600000
end task#5, counter: 700000
start task#6, counter: 700000
end task#6, counter: 800000
start task#7, counter: 800000
end task#7, counter: 900000
start task#8, counter: 900000
end task#8, counter: 1000000
1000000

插入runtime.Gosched()

為了制造點(diǎn)緊張氣氛,我將代碼改寫成下面這樣子,將counter++三條指令明顯寫成三條語(yǔ)句,并在中間插入runtime.Gosched(),故意給其它 goroutine 的執(zhí)行制造機(jī)會(huì):

func TestCounter2() {
    runtime.GOMAXPROCS(1)
 var counter int
 var wg sync.WaitGroup
 wg.Add(10)
 for i := 0; i < 10; i++ {
  i := i
  go func() {
   fmt.Printf("start task#%d, counter: %d\n", i, counter)
   for j := 0; j < 10_0000; j++ {
    temp := counter
    runtime.Gosched()
    temp = temp + 1
    counter = temp
   }
   fmt.Printf("end task#%d, counter: %d\n", i, counter)
   wg.Done()
  }()
 }
 wg.Wait()
 fmt.Println(counter)
}

運(yùn)行這段代碼,你就會(huì)明顯看到數(shù)據(jù)不一致的效果,即使是單個(gè)線程運(yùn)行 goroutine,也出現(xiàn)了數(shù)據(jù)競(jìng)爭(zhēng)的問(wèn)題:

start task#9, counter: 0
start task#0, counter: 0
start task#1, counter: 0
start task#2, counter: 0
start task#3, counter: 0
start task#4, counter: 0
start task#5, counter: 0
start task#6, counter: 0
start task#7, counter: 0
start task#8, counter: 0
end task#9, counter: 100000
end task#1, counter: 100000
end task#3, counter: 100000
end task#2, counter: 100000
end task#5, counter: 100000
end task#0, counter: 100000
end task#4, counter: 100000
end task#6, counter: 100000
end task#7, counter: 100000
end task#8, counter: 100000
100000

這個(gè)結(jié)果非常離譜,期望 100 萬(wàn),最后只有 10 萬(wàn)。

訪問(wèn)同一個(gè) map對(duì)象也有可能出現(xiàn)并發(fā)bug

因?yàn)閱蝹€(gè)線程運(yùn)行多個(gè) goroutine 會(huì)有數(shù)據(jù)競(jìng)爭(zhēng)的問(wèn)題,所以訪問(wèn)同一個(gè) map 對(duì)象也有可能出現(xiàn)并發(fā) bug,比如下面的代碼,10 個(gè) goroutine 并發(fā)的寫同一個(gè) map:

func TestMap() {
 var m = make(map[int]int)
 var wg sync.WaitGroup
 wg.Add(10)
 for i := 0; i < 10; i++ {
  i := i
  go func() {
   fmt.Printf("start map task#%d, m: %v\n", i, len(m))
   for j := 0; j < 10_0000; j++ {
    m[j] = i*10_0000 + j
   }
   fmt.Printf("end map task#%d, m: %v\n", i, len(m))
   wg.Done()
  }()
 }
 wg.Wait()
}

大概率會(huì)出現(xiàn) panic:

start map task#9, m: 0
start map task#0, m: 49152
fatal error: concurrent map writes
goroutine 41 [running]:
main.TestMap.func1()
 /Users/chaoyuepan/study/single_thread/main.go:72 +0xcc
created by main.TestMap in goroutine 1
 /Users/chaoyuepan/study/single_thread/main.go:69 +0x4c
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x140000021a0?)
 /usr/local/go/src/runtime/sema.go:62 +0x2c
sync.(*WaitGroup).Wait(0x1400000e1d0)
 /usr/local/go/src/sync/waitgroup.go:116 +0x74
main.TestMap()
 /Users/chaoyuepan/study/single_thread/main.go:79 +0xb8
main.main()
 /Users/chaoyuepan/study/single_thread/main.go:15 +0x2c

以上就是Go語(yǔ)言單線程運(yùn)行的并發(fā)問(wèn)題解析的詳細(xì)內(nèi)容,更多關(guān)于Go單線程運(yùn)行并發(fā)問(wèn)題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang 獲取明天零點(diǎn)的時(shí)間戳示例

    golang 獲取明天零點(diǎn)的時(shí)間戳示例

    今天小編就為大家分享一篇golang 獲取明天零點(diǎn)的時(shí)間戳示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Go語(yǔ)言微服務(wù)中實(shí)現(xiàn)鏈路追蹤

    Go語(yǔ)言微服務(wù)中實(shí)現(xiàn)鏈路追蹤

    在微服務(wù)架構(gòu)中,鏈路追蹤技術(shù)可以幫助我們跟蹤請(qǐng)求在各個(gè)服務(wù)之間的傳播路徑,本文就來(lái)介紹一下Go語(yǔ)言微服務(wù)中實(shí)現(xiàn)鏈路追蹤,感興趣的可以了解一下
    2024-12-12
  • golang?db事務(wù)的統(tǒng)一封裝的實(shí)現(xiàn)

    golang?db事務(wù)的統(tǒng)一封裝的實(shí)現(xiàn)

    這篇文章主要介紹了golang db事務(wù)的統(tǒng)一封裝的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Golang負(fù)載均衡和?;钤O(shè)計(jì)原理示例探究

    Golang負(fù)載均衡和保活設(shè)計(jì)原理示例探究

    這篇文章主要為大家介紹了Golang負(fù)載均衡和?;钤O(shè)計(jì)原理示例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Golang sync.Map原理深入分析講解

    Golang sync.Map原理深入分析講解

    go中map數(shù)據(jù)結(jié)構(gòu)不是線程安全的,即多個(gè)goroutine同時(shí)操作一個(gè)map,則會(huì)報(bào)錯(cuò),因此go1.9之后誕生了sync.Map,sync.Map思路來(lái)自java的ConcurrentHashMap
    2022-12-12
  • 詳解Go語(yǔ)言如何實(shí)現(xiàn)二叉樹(shù)遍歷

    詳解Go語(yǔ)言如何實(shí)現(xiàn)二叉樹(shù)遍歷

    這篇文章主要為大家詳解介紹了Go語(yǔ)言中如何實(shí)現(xiàn)二叉樹(shù)遍歷,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定幫助,需要的可以參考一下
    2022-04-04
  • Go 并發(fā)讀寫 sync.map 詳細(xì)

    Go 并發(fā)讀寫 sync.map 詳細(xì)

    閱讀本文你將會(huì)明確 sync.Map 和原生 map +互斥鎖/讀寫鎖之間的性能情況。標(biāo)準(zhǔn)庫(kù) sync.Map 雖說(shuō)支持并發(fā)讀寫 map,但更適用于讀多寫少的場(chǎng)景,因?yàn)樗麑懭氲男阅鼙容^差,使用時(shí)要考慮清楚這一點(diǎn)。
    2021-10-10
  • 揭秘Go Json.Unmarshal精度丟失之謎

    揭秘Go Json.Unmarshal精度丟失之謎

    我們知道在json反序列化時(shí)是沒(méi)有整型和浮點(diǎn)型的區(qū)別,數(shù)字都使用同一種類型,在go語(yǔ)言的類型中這種共同類型就是float64,下面我們就來(lái)探討一下Json.Unmarshal精度丟失之謎吧
    2023-08-08
  • GoFrame框架ORM原生方法對(duì)象操作開(kāi)箱體驗(yàn)

    GoFrame框架ORM原生方法對(duì)象操作開(kāi)箱體驗(yàn)

    這篇文章主要為大家介紹了GoFrame框架ORM原生方法對(duì)象操作的開(kāi)箱體驗(yàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 如何利用Golang解析讀取Mysql備份文件

    如何利用Golang解析讀取Mysql備份文件

    這篇文章主要給大家介紹了關(guān)于如何利用Golang解析讀取Mysql備份文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Golang具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12

最新評(píng)論