一、什么是協程(Goroutine)?
簡單來說,協程是由 Go 語言運行時管理的輕量級線程。相比系統線程,它的調度開銷極小,內存占用非常少(默認只需 2KB 棧空間)。
你可以在一個程序中輕松創建成千上萬個 goroutine,而不會像傳統線程那樣造成系統負擔。
二、如何創建一個協程
只需要在函數調用前加上 go?關鍵字,Go 就會在新的協程中異步執行該函數:
package mainimport ("fmt""time"
)func sayHello() {fmt.Println("Hello from goroutine")
}func main() {go sayHello() // 啟動一個新的協程time.Sleep(1 * time.Second) // 給協程執行的時間
}
如果不加 time.Sleep
,主線程可能直接退出,協程還沒執行完,即不會輸出"Hello from goroutine"。
三、多個協程并發執行
我們可以輕松開啟多個任務同時運行:
package mainimport ("fmt""time"
)func main() {for i := 0; i < 5; i++ {go func(i int) {fmt.Printf("Worker %d is running\n", i)}(i)}time.Sleep(1 * time.Second)
}
輸出順序是不確定的,因為每個協程的調度是由 Go 運行時決定的。
四、協程與主線程的關系
主函數是 Go 程序的入口,也是主協程。一旦 main()
執行完畢,程序就退出,即使其他協程還在執行。
為了解決這個問題,我們常用 sync.WaitGroup
等機制來等待所有協程結束:
package mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc worker(id int) {defer wg.Done()fmt.Printf("Worker %d done\n", id)
}func main() {for i := 1; i <= 3; i++ {wg.Add(1)go worker(i)}wg.Wait() // 等待所有 goroutine 完成
}
五、goroutine 的注意事項
閉包中的變量捕獲問題
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroupfunc main() {wg.Add(3)for i := 1; i <= 3; i++ {go func() {time.Sleep(time.Second)fmt.Println(i)wg.Done()}()}wg.Wait() // 等待所有 goroutine 完成
}
正確做法是將變量作為參數傳進去:
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroupfunc main() {wg.Add(3)for i := 1; i <= 3; i++ {go func(val int) {time.Sleep(time.Second)fmt.Println(val)wg.Done()}(i)}wg.Wait() // 等待所有 goroutine 完成
}