Go 语言实现的协程
Go 语言简单实现协程
Go 语言中的协程叫 goroutine。它是一种轻量级线程,由 Go 运行时负责调度。相比系统线程,goroutine 创建成本更低,写并发程序也更简单。
启动一个协程只需要在函数调用前加上 go 关键字:
go task()
1. 最简单的 goroutine
下面的例子中,sayHello 会在新的协程中执行。
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("hello goroutine")
}
func main() {
go sayHello()
time.Sleep(time.Second)
fmt.Println("main finished")
}
说明:
go sayHello()会启动一个新的goroutine。main函数本身也是一个goroutine。- 如果
main结束,其他还没执行完的goroutine也会被直接结束。 - 这里用
time.Sleep是为了简单等待子协程执行完,但真实项目更推荐使用sync.WaitGroup。
2. 使用 WaitGroup 等待协程结束
sync.WaitGroup 用来等待一组协程执行完成。
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("worker", id, "running")
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("all workers finished")
}
说明:
wg.Add(1)表示新增一个需要等待的任务。defer wg.Done()表示当前协程结束时通知WaitGroup。wg.Wait()会阻塞,直到所有任务都执行完成。
3. 使用 channel 在协程间通信
channel 是 Go 中用于协程之间通信的管道。一个协程可以向 channel 发送数据,另一个协程可以从中接收数据。
package main
import "fmt"
func producer(ch chan string) {
ch <- "hello from producer"
}
func main() {
ch := make(chan string)
go producer(ch)
message := <-ch
fmt.Println(message)
}
说明:
make(chan string)创建一个字符串类型的通道。ch <- value表示向通道发送数据。value := <-ch表示从通道接收数据。- 默认情况下,发送和接收都会阻塞,直到另一端准备好。
4. 综合示例:多个协程计算结果
下面的例子启动多个协程分别计算数字平方,然后通过 channel 把结果传回主协程。
package main
import (
"fmt"
"sync"
)
func square(n int, ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
ch <- n * n
}
func main() {
numbers := []int{1, 2, 3, 4, 5}
ch := make(chan int)
var wg sync.WaitGroup
for _, n := range numbers {
wg.Add(1)
go square(n, ch, &wg)
}
go func() {
wg.Wait()
close(ch)
}()
for result := range ch {
fmt.Println("result:", result)
}
}
说明:
- 每个数字都由一个独立的
goroutine处理。 - 子协程通过
channel把结果发送给主协程。 - 所有子协程结束后关闭
channel。 for result := range ch会一直读取通道,直到通道被关闭。
小结
Go 实现协程主要靠三个核心点:
| 语法或类型 | 作用 |
|---|---|
go func() |
启动一个新的协程 |
sync.WaitGroup |
等待多个协程执行完成 |
channel |
在协程之间传递数据 |
简单记忆:goroutine 负责并发执行,WaitGroup 负责等待结束,channel 负责通信。

浙公网安备 33010602011771号