<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go:從一個(gè)data race問(wèn)題學(xué)到的

          共 2380字,需瀏覽 5分鐘

           ·

          2022-06-09 05:38

          前幾天我在學(xué)習(xí)內(nèi)存屏障[1]的時(shí)候搜到一篇文章「Golang Memory Model[2]」,其中在介紹 CPU 緩存一致性的時(shí)候提到一個(gè)例子,帶給我一些困惑,本文記錄下解惑過(guò)程。

          既然是在介紹 CPU 緩存一致性的時(shí)候舉的例子,那么理所應(yīng)當(dāng)與此有關(guān),看代碼:

          package?main

          import?"time"

          func?main()?{
          ?running?:=?true
          ?go?func()?{
          ??println("start?thread1")
          ??count?:=?1
          ??for?running?{
          ???count++
          ??}
          ??println("end?thread1:?count?=",?count)
          ?}()
          ?go?func()?{
          ??println("start?thread2")
          ??for?{
          ???running?=?false
          ??}
          ?}()
          ?time.Sleep(time.Hour)
          }

          當(dāng)我們通過(guò)「go run main.go」運(yùn)行代碼的時(shí)候,會(huì)發(fā)現(xiàn)第一個(gè) goroutine 永遠(yuǎn)不會(huì)結(jié)束,就好像 running = false 沒(méi)有生效一樣。對(duì)此,文章把原因歸結(jié)為 CPU 緩存一致性中的線(xiàn)程可見(jiàn)性問(wèn)題,可是我前后看了幾遍也沒(méi)有看出個(gè)所以然來(lái)。細(xì)心的小伙伴不難發(fā)現(xiàn)代碼存在 data race 問(wèn)題:多個(gè) goroutine 并發(fā)讀寫(xiě) running 變量,不過(guò)當(dāng)我們通過(guò)「go run -race main.go」再次運(yùn)行代碼的時(shí)候,有趣的事情發(fā)生了,第一個(gè) goroutine 正常結(jié)束了!

          理論上,既然存在 data race 問(wèn)題,那么出現(xiàn)什么結(jié)果都可能,但是好奇心驅(qū)使我繼續(xù)研究了一下,這次使用的工具是 SSA[3],它可以展現(xiàn)出從源代碼到匯編的過(guò)程中,編譯器都做了哪些工作,并且可以把結(jié)果生成 html 文件:

          shell>?GOSSAFUNC=main?go?build?-gcflags="-N?-l"?./main.go

          SSA 工具最方便的地方是它可以把源代碼和匯編通過(guò)顏色對(duì)應(yīng)起來(lái):

          main 函數(shù)的 ssa

          說(shuō)明:Golang 中的匯編一般指 Plan9 匯編,推薦閱讀「plan9 assembly 完全解析[4]」。

          不過(guò)為什么「running = false」這行源代碼沒(méi)有對(duì)應(yīng)的匯編呢?這是因?yàn)?SSA 的工作單位是函數(shù),上面的結(jié)果是 main 函數(shù)的結(jié)果,而「running = false」實(shí)際上屬于 main 函數(shù)里第 2 個(gè) goroutine,實(shí)際上就相當(dāng)于 main.func2,重新運(yùn)行 SSA:

          shell>?GOSSAFUNC=main.func2?go?build?-gcflags="-l?-N"?./main.go

          如此一來(lái)就能看到「running = false」這行源代碼對(duì)應(yīng)的匯編了:

          main.func2 函數(shù)的 ssa

          其中,PCDATA 是編譯器插入的和 GC 相關(guān)的信息,在本例中可以忽略,剩下的幾個(gè) JMP 跳來(lái)跳去,好像是個(gè)圈哦,就是一個(gè)空 for,和「running = false」完全沒(méi)有關(guān)系。

          不過(guò)既然帶有 race 檢測(cè)的代碼工作正常,那么不妨一并生成 SSA 看看結(jié)果如何:

          shell>?GOSSAFUNC=main.func2?go?build?-race?-gcflags="-l?-N"?./main.go

          結(jié)果如下圖所示,出了 JMP,還有 MOV 操作,正好對(duì)應(yīng)「running = false」:

          main.func2 函數(shù)的 ssa

          如此一來(lái),我們的困惑終于解開(kāi)了。問(wèn)題代碼中的循環(huán)之所以不會(huì)結(jié)束,和所謂的「CPU 緩存一致性中的線(xiàn)程可見(jiàn)性問(wèn)題」并沒(méi)有任何關(guān)系,只是因?yàn)榫幾g器把部分代碼看成死代碼,直接優(yōu)化掉了,這個(gè)過(guò)程稱(chēng)之為「Dead code elimination[5]」,不過(guò)當(dāng)激活 race 檢測(cè)的時(shí)候,編譯器并沒(méi)有執(zhí)行死代碼的優(yōu)化,所以程序看上去又正常了。

          最后,推薦一篇文章,和本文的例子相似:談?wù)?Golang 中的 Data Race[6](及續(xù)[7])。

          參考資料

          [1]

          內(nèi)存屏障: https://zh.wikipedia.org/wiki/%E5%86%85%E5%AD%98%E5%B1%8F%E9%9A%9C

          [2]

          Golang Memory Model: https://fanlv.wiki/2020/06/09/golang-memory-model/

          [3]

          SSA: https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/README.md

          [4]

          plan9 assembly 完全解析: https://github.com/cch123/golang-notes/blob/master/assembly.md

          [5]

          Dead code elimination: https://en.wikipedia.org/wiki/Dead_code_elimination

          [6]

          談?wù)?Golang 中的 Data Race: https://ms2008.github.io/2019/05/12/golang-data-race/

          [7]

          續(xù): https://ms2008.github.io/2019/05/22/golang-data-race-cont/



          推薦閱讀


          福利

          我為大家整理了一份從入門(mén)到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門(mén)看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 85
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91九色中文 | www.一级内射 | 偷拍无码网站 | 日韩A片在线 | 大香蕉欧美在线 |