插件窝 干货文章 Go语言中如何用单个Channel保证多个Goroutine同步输出?

Go语言中如何用单个Channel保证多个Goroutine同步输出?

令牌 Goroutine 传递 输出 929    来源:    2025-03-11

在Go语言中,可以使用单个Channel通过传递令牌的方式保证多个Goroutine按顺序同步输出。以下是具体实现方案:

核心思路

  1. 令牌传递:通过Channel传递一个控制令牌(Token),只有持有令牌的Goroutine有权执行输出。
  2. 接力机制:每个Goroutine执行完毕后,将令牌传递给下一个指定Goroutine,形成接力链条。

实现代码

package main

import (
    "fmt"
    "time"
)

func worker(ch chan string, id string, nextID string) {
    for {
        token := <-ch // 等待令牌
        if token == id {
            fmt.Println(id)          // 执行输出
            if nextID != "" {       // 传递令牌给下一个
                ch <- nextID
            }
            return // 当前Goroutine任务完成
        } else {
            ch <- token // 非当前令牌,放回Channel
        }
    }
}

func main() {
    ch := make(chan string, 1)
    ch <- "A" // 初始化令牌

    // 启动三个Goroutine,按A->B->C顺序执行
    go worker(ch, "A", "B")
    go worker(ch, "B", "C")
    go worker(ch, "C", "") // 最后一个不传递令牌

    // 等待足够时间确保所有Goroutine执行完毕
    time.Sleep(1 * time.Second)
}

关键点解析

  1. 令牌初始化:Channel初始放入第一个令牌(如"A"),启动第一个Goroutine的执行。
  2. 令牌校验:每个Goroutine循环读取Channel,检查令牌是否匹配自己的ID。
  3. 接力传递:匹配成功后输出内容,并将令牌传递给下一个Goroutine(如A→B→C)。
  4. 竞争处理:未匹配的Goroutine立即将令牌放回Channel,确保令牌持续流转。

执行流程

  1. Goroutine A 获取令牌"A",输出"A",传递令牌"B"。
  2. Goroutine B 获取令牌"B",输出"B",传递令牌"C"。
  3. Goroutine C 获取令牌"C",输出"C",结束传递。

方案优势

  • 严格顺序:通过令牌强制串行化,确保输出顺序。
  • 资源高效:仅使用单个Channel,避免复杂同步结构。
  • 动态扩展:通过调整令牌传递链,可轻松增减Goroutine数量。

此方案通过巧妙的令牌传递机制,实现了多Goroutine的同步顺序控制,是单Channel同步的经典应用场景。