Go 并发入门:理解Channel的发送、接收和等待
运行两个独立任务并汇总结果,通过发送与接收的配对理解同步关系,避免把Sleep误当作可靠的等待机制。
本节目标
运行两个独立任务并汇总结果,通过发送与接收的配对理解同步关系,避免把Sleep误当作可靠的等待机制。
前置知识
理解函数调用、匿名函数和变量。只展示并发交接,不把这个示例当作完整任务池。
准备与运行
在一个新的练习目录执行以下命令。目录名可以修改;若该目录已经初始化过模块,不要重复执行go mod init。
mkdir go-lesson-10
cd go-lesson-10
go mod init example.com/devcn/lesson10将下方完整代码保存为当前目录的main.go,然后执行:
go fmt ./...
go run .先用go version确认工具链可用。本例在Go 1.25.7、macOS ARM64环境验证,只使用标准库;没有测试所有操作系统和Go版本。
核心概念
go语句启动一个goroutine执行函数,调用方不会等待这个函数正常返回后才继续下一行。并发不保证两个任务的实际完成顺序,也不意味着所有机器上都必然同时执行。
本例使用无缓冲channel。发送需要与接收配对,发送方在接收方准备好之前会等待。main接收两次,因此只有两个结果都交接完成后,才会计算出最终总和。
两个任务不修改同一个共享变量,而是各自把结果发给main,由main完成累加。这样可以从数据流看清谁负责计算、谁负责汇总。
完整示例
package main
import "fmt"
func main() {
ch := make(chan int)
go func() { ch <- 6 * 6 }()
go func() { ch <- 8 * 8 }()
total := <-ch
total += <-ch
fmt.Println(total)
}运行后应得到:
100代码如何工作
第一个任务发送36,第二个发送64。第一次接收可能得到任意一个值,第二次得到另一个;因为最终是加法,总结果始终为100。
代码没有关闭channel,因为没有接收方需要通过关闭信号判断结束,接收次数已经明确。关闭channel不是每次使用后都必须执行的资源释放动作;它表达的是发送结束这一协议事件。
main完成后程序退出。这个例子不包含超时、取消、任务失败或并发数量限制,生产任务处理需要另外设计这些行为。
常见错误与边界
- 用固定Sleep等待任务,既可能等待过长,也可能在慢机器上提前结束。
- 把接收次数改成3、仍只发送2次,main会等待缺失的第三个结果,程序可能报告死锁。
- 只接收1次并立即退出main时,另一个发送者可能还在等待,但进程会随main结束,不一定看到死锁提示。
- 发送者仍可能发送时关闭通道,会导致发送到已关闭通道的panic。
练习
增加一个发送25的任务,把接收次数改成3,确认总和始终为125。
参考答案
用固定次数循环汇总更容易与任务数对应:
ch := make(chan int)
for _, value := range []int{36, 64, 25} {
go func(n int) { ch <- n }(value)
}
total := 0
for i := 0; i < 3; i++ { total += <-ch }
fmt.Println(total)参数n显式接收每轮数值,不依赖循环变量捕获行为。尝试多次运行,结果应为125,但不要用这个结果推断三个任务的实际完成顺序。
文章回复
0 条公开回复