DevCN
菜单
全部文章快讯开发科技深度热点

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,但不要用这个结果推断三个任务的实际完成顺序。

下载推广海报

文章推广海报

《Go 并发入门:理解Channel的发送、接收和等待》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。