引入
普通程序一次只做一件事,从头到尾按顺序执行。但很多时候需要同时处理多个任务——比如服务器同时响应多个请求。Go 从语言层面原生支持并发,用 goroutine 就能轻松启动一个并发执行单元,不需要手动管理线程。
正文
定义
定义
并发(concurrency)是程序同时处理多件事的能力,强调任务的结构和调度。并行(parallelism)是多件事真正同时执行,依赖多核 CPU。goroutine 是 Go 的轻量级并发执行单元,用
go关键字启动,由 Go 运行时调度,比操作系统线程开销小得多。
语法
// 启动一个 goroutine
go func() {
fmt.Println("在另一个 goroutine 中运行")
}()
// 启动一个普通函数作为 goroutine
go myFunc(arg1, arg2)例子
启动 goroutine
func sayHello() {
fmt.Println("hello")
}
func main() {
go sayHello()
time.Sleep(100 * time.Millisecond)
}在函数调用前加 go 就启动了一个 goroutine,主函数不会等它执行完。这里用 time.Sleep() 让主函数等一下,否则程序直接退出,goroutine 来不及执行。
匿名函数 goroutine
func main() {
go func() {
for i := 0; i < 3; i++ {
fmt.Println("goroutine:", i)
}
}()
for i := 0; i < 3; i++ {
fmt.Println("main:", i)
}
time.Sleep(100 * time.Millisecond)
}匿名函数也能用 go 直接启动,适合短小的并发任务。
多个 goroutine
func worker(id int) {
fmt.Printf("worker %d 开始\n", id)
time.Sleep(100 * time.Millisecond)
fmt.Printf("worker %d 完成\n", id)
}
func main() {
for i := 1; i <= 3; i++ {
go worker(i)
}
time.Sleep(200 * time.Millisecond)
}多个 goroutine 并发执行,完成顺序不确定,每次运行结果可能不同。
goroutine 传参
func greet(name string) {
fmt.Println("hello", name)
}
func main() {
go greet("Alice")
go greet("Bob")
time.Sleep(100 * time.Millisecond)
}启动 goroutine 时传入参数,每个 goroutine 持有自己独立的参数副本。
goroutine 闭包陷阱
func main() {
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i) // 可能全部输出 3
}()
}
time.Sleep(100 * time.Millisecond)
}闭包直接引用循环变量 i,goroutine 执行时 i 可能已经变成了最终值。解决办法是传参:
for i := 0; i < 3; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}常见写法
| 写法 | 说明 |
|---|---|
go func() { … }() | 匿名函数启动 goroutine |
go myFunc(args) | 普通函数启动 goroutine |
time.Sleep(d) | 临时等待(仅调试用) |
runtime.NumGoroutine() | 获取当前 goroutine 数量 |
runtime.GOMAXPROCS(n) | 设置最大并行数 |
特点
goroutine是 Go 的轻量级并发执行单元,用go关键字启动- 初始栈只有 2KB,可以轻松创建数万个
- 由 Go 运行时调度,不是操作系统线程
- 主
goroutine(main函数)退出后,其他goroutine直接终止 goroutine之间没有父子关系,无法从一个goroutine控制另一个- 闭包中引用循环变量要注意传参避免陷阱
time.Sleep()只是临时手段,正式代码应该用sync.WaitGroup或channel来协调
理解
goroutine 就是 Go 给并发开的一条快车道。加个 go 关键字就能跑起来,不用操心线程池、回调这些东西。但要注意,goroutine 启动后主函数不等它,怎么让它们协调好、怎么安全地共享数据,就需要 channel 和各种同步工具了。
引出
goroutine 启动了,但多个 goroutine 之间怎么传递数据?接下来看 Go channel,学习 Go 里 goroutine 之间通信的管道。