在 Go 中,使用两个协程(goroutine)交替打印 123456
的核心在于同步机制和流程控制。我们可以借助 channel 来实现两个 goroutine 之间的协作,确保它们按顺序交替打印。
✅ 实现目标
- 使用两个 goroutine 交替打印数字 1 到 6;
- 输出顺序为:1 → 2 → 3 → 4 → 5 → 6;
- 每个 goroutine 负责打印一半的数字(比如,一个打印奇数,一个打印偶数);
- 打印结束后程序应正确退出。
🛠 实现思路
- 共享计数器:使用
atomic
包管理一个共享的计数器变量,确保并发读写安全。 - 通道通信:使用两个无缓冲的 channel(
aChan
和bChan
)作为“令牌”,控制 goroutine 的执行顺序。 - 流程控制:
- 每个 goroutine 在接收到 channel 信号后打印当前数字;
- 打印完成后,将“令牌”传递给另一个 goroutine;
- 当数字达到 6 时,两个 channel 被关闭,goroutine 退出循环;
- 主函数通过
WaitGroup
等待两个 goroutine 完成。
🧩 示例代码
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int32 = 1
aChan := make(chan bool)
bChan := make(chan bool)
var wg sync.WaitGroup
wg.Add(2)
// Goroutine A 打印奇数
go func() {
defer wg.Done()
for {
_, ok := <-aChan
if !ok {
return
}
current := atomic.LoadInt32(&counter)
fmt.Print(current, " ")
newCounter := current + 1
atomic.StoreInt32(&counter, newCounter)
if newCounter <= 6 {
bChan <- true
} else {
// 关闭两个 channel,通知所有 goroutine 退出
close(aChan)
close(bChan)
}
}
}()
// Goroutine B 打印偶数
go func() {
defer wg.Done()
for {
_, ok := <-bChan
if !ok {
return
}
current := atomic.LoadInt32(&counter)
fmt.Print(current, " ")
newCounter := current + 1
atomic.StoreInt32(&counter, newCounter)
if newCounter <= 6 {
aChan <- true
} else {
// 关闭两个 channel,通知所有 goroutine 退出
close(aChan)
close(bChan)
}
}
}()
// 启动流程,由 Goroutine A 开始打印 1
aChan <- true
// 等待两个 goroutine 完成
wg.Wait()
}
📌 输出结果
运行程序后,输出如下(顺序严格交替):
1 2 3 4 5 6
🔍 说明
atomic.LoadInt32
/atomic.StoreInt32
:用于确保共享变量的并发访问安全。aChan
/bChan
:作为“令牌”控制 goroutine 的执行顺序。每个 goroutine 打印完后将“令牌”传递给对方。close(aChan)
/close(bChan)
:在最后一个数字打印完后关闭 channel,使 goroutine 退出循环,避免阻塞和 goroutine 泄漏。sync.WaitGroup
:用于主函数等待两个 goroutine 完成,确保程序不会提前退出。
✅ 小结
通过使用 channel 和原子操作,我们可以安全地实现两个 goroutine 的交替打印,并保证顺序和程序的正确退出。该方法结构清晰、同步机制简单有效,适用于类似的并发控制场景。