Go 协程(Goroutine)入门与基础使用
一、什么是协程(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 完成
}