偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

在 Go 中調(diào)用 go func() 時究竟發(fā)生了什么

開發(fā) 后端
每當(dāng)你鍵入 go func(),實(shí)際上啟動的是由高效調(diào)度器管理的“迷你進(jìn)程”。本篇文章將揭開這一機(jī)制的面紗,說明 goroutine 并非簡單的 pthread_create() 封裝。

當(dāng)你在 Go 代碼中輸入 go func() 時,表面上似乎只是啟動了一個后臺線程;實(shí)際上,這一指令觸發(fā)了運(yùn)行時調(diào)度器、操作系統(tǒng)線程與一系列精妙機(jī)制之間的協(xié)同運(yùn)作。本篇文章將揭開這一機(jī)制的面紗,說明 goroutine 并非簡單的 pthread_create() 封裝。

示例程序

func main() {
    go sayHello()
    time.Sleep(1 * time.Second) // 讓 goroutine 有機(jī)會運(yùn)行
}

func sayHello() {
    fmt.Println("Hello from a goroutine!")
}

go 關(guān)鍵字讓上述代碼看似平凡。然而內(nèi)部流程遠(yuǎn)比創(chuàng)建一個普通線程復(fù)雜得多。

運(yùn)行時核心:G-M-P 調(diào)度模型

Go 采用 M:N 調(diào)度器,通過 G-M-P 三元組實(shí)現(xiàn)高并發(fā)而低開銷的調(diào)度。

  • G (Goroutine)
  • M (Machine):操作系統(tǒng)線程
  • P (Processor):邏輯處理器,負(fù)責(zé)調(diào)度
Goroutines (G)
        ↓↓↓↓↓
+-------------------+
|  Processors (P)   |   每個 P 維護(hù)本地可運(yùn)行隊(duì)列
+-------------------+
        ↓↓↓↓↓
     OS Threads (M)

運(yùn)行時將大量 G 映射到有限數(shù)量的 P,而 P 又綁定到真正的系統(tǒng)線程 M。該設(shè)計(jì)允許在有限資源下高效調(diào)度成千上萬的 goroutine。

go func() 的內(nèi)部步驟

(1) 編譯期轉(zhuǎn)換:源碼 go sayHello() 被編譯器轉(zhuǎn)換為運(yùn)行時調(diào)用

runtime.newproc(fnPointer, arguments)

(2) 創(chuàng)建新的 G:newproc 為函數(shù)及其參數(shù)分配一個新的 G 結(jié)構(gòu)體,并將其壓入當(dāng)前 P 的本地運(yùn)行隊(duì)列。

(3) 調(diào)度到 M:每個活躍的 P 綁定一個正在運(yùn)行的 M。M 從本地隊(duì)列中取出 G 執(zhí)行;若隊(duì)列為空,則嘗試從全局隊(duì)列或其他 P 的隊(duì)列中“工作竊取”。

Goroutine 結(jié)構(gòu)體(G)的關(guān)鍵字段

  • 棧指針與棧邊界
  • 程序計(jì)數(shù)器
  • 狀態(tài)標(biāo)志:_Grunnable、_Grunning、_Gwaiting 等
  • defer 與 panic 處理信息
  • 鏈表指針,用于排隊(duì)或調(diào)度

(1) 棧的動態(tài)增長

每個 goroutine 以約 2 KB 的微小棧啟動,并按需擴(kuò)展:

2 KB → 4 KB → 8 KB → …

動態(tài)棧使生成數(shù)百萬個 goroutine 成為可能,而不會占用過多內(nèi)存。

流程示意圖

main.go
 └─> go sayHello()
       └─> runtime.newproc()
             └─> allocate new G
             └─> push to P's run queue
                   └─> M picks G from queue
                         └─> executes sayHello()

真實(shí)示例與輸出

func main() {
    for i := 0; i < 3; i++ {
        go func(i int) {
            fmt.Printf("Worker %d starting\n", i)
            time.Sleep(time.Second)
            fmt.Printf("Worker %d done\n", i)
        }(i)
    }
    time.Sleep(2 * time.Second)
}

預(yù)期輸出(順序可能不同):

Worker 0 starting
Worker 2 starting
Worker 1 starting
Worker 2 done
Worker 1 done
Worker 0 done

調(diào)度器采用搶占式策略,故輸出順序不確定。

常見陷阱

  • 數(shù)據(jù)競爭:輕易生成 goroutine 不代表可以隨意共享內(nèi)存。務(wù)必使用通道或同步原語保護(hù)共享數(shù)據(jù)。
  • Goroutine 泄漏:若 goroutine 永久阻塞(如等待一個永不寫入的通道),將持續(xù)占用內(nèi)存。
  • 調(diào)度器爭用:數(shù)百萬個忙等待 goroutine 仍可能導(dǎo)致饑餓。

建議使用 pprof、runtime.NumGoroutine() 及 context 取消機(jī)制管理生命周期。

基準(zhǔn):goroutine 的成本

func BenchmarkGoroutines(b *testing.B) {
    for i := 0; i < b.N; i++ {
        done := make(chan bool)
        go func() { done <- true }()
        <-done
    }
}

在 Apple M1 Mac 上的觀測結(jié)果:

  • 創(chuàng)建并運(yùn)行一個 goroutine ≈ 200 ns
  • 100 萬個空閑 goroutine 占用約 10 MB 內(nèi)存

相比每個 OS 線程動輒 1 MB 以上的??臻g,優(yōu)勢顯著。

結(jié)語

Go 之美在于用看似簡單的語法抽象隱藏了復(fù)雜的系統(tǒng)編程哲學(xué)。每當(dāng)你鍵入 go func(),實(shí)際上啟動的是由高效調(diào)度器管理的“迷你進(jìn)程”。下次當(dāng)應(yīng)用輕松生成十萬級 goroutine 時,不妨放心微笑——Go 運(yùn)行時自會為你撐腰。

責(zé)任編輯:趙寧寧 來源: 令飛編程
相關(guān)推薦

2023-03-31 08:12:30

操作系統(tǒng)nanosleep信號

2022-06-03 08:12:52

InnoDB插入MySQL

2019-11-12 14:41:41

Redis程序員Linux

2021-01-18 08:23:23

內(nèi)存時底層CPU

2021-01-21 14:09:28

云計(jì)算華為云

2021-11-23 23:31:43

C語言數(shù)據(jù)類型系統(tǒng)

2020-08-20 11:50:31

語言類型轉(zhuǎn)換代碼

2011-03-31 09:20:45

URLDNSWeb應(yīng)用程序

2018-03-16 15:31:50

2018-08-28 15:42:42

顯卡NVIDIA廠商

2021-06-30 06:02:38

MySQL SQL 語句數(shù)據(jù)庫

2018-07-23 14:43:56

數(shù)據(jù)庫DBAMySQL

2019-03-14 11:00:40

GoLua語言

2025-06-30 09:26:47

2020-08-17 12:47:07

Mozilla裁員瀏覽器

2024-02-23 08:38:34

AI模型計(jì)算機(jī)

2024-03-19 14:15:48

Go程序os.Exit()

2024-01-09 11:56:58

Go編程語言

2023-02-26 23:36:08

PHPGo函數(shù)

2019-08-26 09:35:25

命令ping抓包
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號