日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

      歡迎訪問 生活随笔!

      生活随笔

      當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

      生活经验

      Go 分布式学习利器(17)-- Go并发编程之协程机制:Grountine 原理及使用

      發布時間:2023/11/27 生活经验 37 豆豆
      生活随笔 收集整理的這篇文章主要介紹了 Go 分布式学习利器(17)-- Go并发编程之协程机制:Grountine 原理及使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

      文章目錄

        • 1. Thread VS Groutine
        • 2. Groutine 調度原理
        • 3. Groutine 示例代碼


      關于Go的底層實現還需要后續持續研究,文中如有一些原理描述有誤,歡迎指證。

      1. Thread VS Groutine

      這里主要介紹一下Go的并發協程相比于傳統的線程 的不同點:

      1. 創建時默認的stack大小
        JDK5 以后Java thread stack默認大小為1M
        C++ 的thread stack 默認大小為8M
        Grountine 的 Stack初始化大小為2K

        所以Grountine 大批量創建的時候速度會更快

      2. 和 KSE(Kernel Space Entity即內核線程)的對應關系
        Java Thread是1:1
        Groutine 是M:N,多對多,如下圖。


        內核線程是由CPU直接調度,如果一個用戶線程對應一個內核線程,調度效率來看肯定是快于多個用戶線程對應一個內核線程的。然而,實際的開發環境中一個用戶線程對應一個內核線程 在 高并發場景下出現的頻繁內核線程上下文切換(保留線程上下文,更新CPU內部各種寄存器)對系統性能的影響占主要部分。而Go語言內部實現的線程調度器提供了多個用戶線程和一個內核線程對應,這樣在高并發場景能夠有效降低線程間切換帶來的性能消耗。
        當然,如果如果僅僅只有幾個或者十幾個(小于CPU核數)用戶線程的應用可能就體現不出Grountine的優勢了。

      2. Groutine 調度原理

      如下圖:

      • 編號1: 沒有被用戶線程使用的內核線程
      • 編號2: Go自實現的協程調度器P 對應一個內核線程M0。在該調度器中維護了一個協程隊列,一個時刻可以有一個協程正在運行G0,其他的協程在調度隊列中等待被執行。
      • 編號3: 協程處理器沒有正在執行的協程,可以從調度隊列中取出一個協程
      • 編號4: 協程可以通過系統調用來和內核線程綁定

      問題1:如果一個正在運行的G協程將Processor 獨占時間較久,導致后續排隊的協程一直無法運行,Processor如何處理?
      Go 運行之后,后臺會維護一個守護線程來進行計數 ,表示一個Processor 完成的協程數量。當一段時間發現這個計數沒有更新,會向當前正在執行的協程任務棧插入一個flag,協程運行時遇到非inline函數時會讀取到這個標記,會將正在運行的自己中斷并插入到等待Processor調度的隊尾,Processor此時會繼續調度其他的協程進行運行。

      問題2: 當一個協程(內核線程的執行)被系統中斷(CPU需要調度I/O線程),Processor會有什么樣的行為?
      Processor并不會就此等待中斷處理完成,而是為了保證并發,將自己和另一個內核線程綁定,繼續執行調度隊列中的等待被執行的協程。當中斷的協程被喚醒,則會將自己加入到另一個Processor的等待隊列或者全局等待隊列中。

      當一個協程被中斷,它在CPU寄存器中的狀態會被保存在自己的協程對象中,重新獲得執行機會時這一些狀態會被寫入到新的寄存器中繼續運行。

      通過以上調度可以看到,Groutine能夠在不同的場景下仍然希望保持較高的并發繼續執行來保證自己的高并發性能。

      3. Groutine 示例代碼

      啟動一個Groutine非常簡單
      go func(){}() 的方式

      測試代碼如下, 注意使用方法一的值傳遞方式啟動go routine 。

      package groutine_testimport ("fmt""testing""time"
      )func TestGroutine(t *testing.T) {for i := 0; i < 10; i ++ {// 方法一: 正確go func(i int) { // 啟動 一個 go routinefmt.Println(i)}(i)// 方法二:錯誤// 如下代碼是有問題的// i 地址是被所有協程共享的,這個時候打印的結果// 會受到其他協程的影響// 想要保證代碼的正確性,即每一個go routine打印// 各自的i 值,需要利用如上啟動go routine的代碼,// 進行值傳遞,從而讓每個goroutine 獨享各自的i的地址。// go func() {//	fmt.Println(i)// }()}time.Sleep(time.Millisecond*50)
      }
      

      總結

      以上是生活随笔為你收集整理的Go 分布式学习利器(17)-- Go并发编程之协程机制:Grountine 原理及使用的全部內容,希望文章能夠幫你解決所遇到的問題。

      如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。