Go 并发入门:goroutine 与 channel

Go 语言最大的卖点之一就是写并发代码很轻松。这篇文章用几个能直接运行的小例子,说明 goroutine 和 channel 的基本用法。

启动一个 goroutine

在函数调用前面加上 go 关键字,这个函数就会在新的 goroutine 里运行,当前代码不会停下来等它。

package main

import (
	"fmt"
	"time"
)

func sayHello(name string) {
	fmt.Println("你好,", name)
}

func main() {
	go sayHello("微风太阳")
	// 主函数一结束整个程序就退出了,这里先简单等一下
	time.Sleep(100 * time.Millisecond)
}

用 time.Sleep 等待只是为了演示,真实项目里应该用 sync.WaitGroup 或 channel 来等待。

用 channel 传递数据

channel 可以理解成两个 goroutine 之间的一根管道:一边往里放,另一边从里面取。

无缓冲 channel

无缓冲 channel 要求发送方和接收方“同时到场”,任何一方先到都会停下来等另一方。

resultChannel := make(chan int)

go func() {
	resultChannel <- 42
}()

result := <-resultChannel
fmt.Println(result) // 42

有缓冲 channel

有缓冲 channel 里面可以先存几个值,存满之前发送方不用等。

taskChannel := make(chan string, 3)
taskChannel <- "任务一"
taskChannel <- "任务二"
fmt.Println(len(taskChannel)) // 2

常见的坑

现象 原因
程序报错 all goroutines are asleep - deadlock! 往无缓冲 channel 发数据,但没有人接收
程序直接退出,goroutine 里的内容没打印 main 函数先结束了
关闭 channel 时 panic 对同一个 channel 关闭了两次

小结

  • go 函数() 启动并发任务。
  • channel 负责在 goroutine 之间传数据。
  • 等待多个任务结束,用 sync.WaitGroup。