全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-13 8 分钟 ✍️ juanwangdev

Go Channel同步与多路复用详解

Channel是goroutine间通信和同步的主要机制。

Channel同步机制

无缓冲channel同步

Go
// 无缓冲channel:发送等待接收
ch := make(chan int)

go func() {
    ch <- 1  // 阻塞等待接收
}()

<-ch  // 阻塞等待发送
// 双方同时就绪才能继续

有缓冲channel异步

Go
// 有缓冲channel:缓冲满才阻塞
ch := make(chan int, 3)

ch <- 1  // 不阻塞(缓冲未满)
ch <- 2
ch <- 3
ch <- 4  // 阻塞(缓冲满)

v := <-ch  // 取出1,ch <- 4继续
类型发送行为接收行为用途
无缓冲等待接收等待发送同步信号
有缓冲缓冲满阻塞缓冲空阻塞数据传递

Select多路复用

基本用法

Go
ch1 := make(chan int)
ch2 := make(chan int)

select {
case v := <-ch1:
    fmt.Println("ch1:", v)
case v := <-ch2:
    fmt.Println("ch2:", v)
default:
    fmt.Println("无数据")
}

非阻塞操作

Go
select {
case ch <- data:
    // 发送成功
default:
    // 发送失败(缓冲满)
}

循环select

Go
for {
    select {
    case v := <-ch1:
        process1(v)
    case v := <-ch2:
        process2(v)
    case <-quit:
        return  // 退出循环
    }
}

超时控制

time.After

Go
select {
case v := <-ch:
    fmt.Println("收到:", v)
case <-time.After(5 * time.Second):
    fmt.Println("超时")
}

time.NewTimer可重用

Go
timer := time.NewTimer(5 * time.Second)

for {
    select {
    case v := <-ch:
        fmt.Println("收到:", v)
        timer.Reset(5 * time.Second)  // 重置
    case <-timer.C:
        fmt.Println("超时")
        return
    }
}

Channel关闭处理

关闭channel

Go
ch := make(chan int, 5)

// 发送者关闭
ch <- 1
ch <- 2
close(ch)

// 接收者检测关闭
for v := range ch {
    fmt.Println(v)  // 1, 2
}
// 自动退出,不会阻塞

判断channel关闭

Go
v, ok := <-ch
if ok {
    fmt.Println("收到:", v)
} else {
    fmt.Println("channel已关闭")
}

// 从已关闭channel接收返回零值+false

双向关闭检测

Go
select {
case v, ok := <-ch:
    if !ok {
        fmt.Println("ch已关闭")
        return
    }
    fmt.Println(v)
}

只有发送者关闭channel,接收者不要关闭。

多路复用模式

优先级选择

Go
// select无优先级,随机选择就绪case
// 模拟优先级:

for {
    select {
    case v := <-priority:
        // 优先处理
    default:
        select {
        case v := <-normal:
            // 正常处理
        case <-priority:
            // 检查优先channel
        }
    }
}

扇入模式

Go
func fanIn(ch1, ch2 <-chan int) <-chan int {
    out := make(chan int)

    go func() {
        for {
            select {
            case v := <-ch1:
                out <- v
            case v := <-ch2:
                out <- v
            }
        }
    }()

    return out
}

扇出模式

Go
func fanOut(ch <-chan int) []chan int {
    outputs := make([]chan int, 3)

    for i := 0; i < 3; i++ {
        outputs[i] = make(chan int)
        go func(out chan<- int) {
            for v := range ch {
                out <- v
            }
        }(outputs[i])
    }

    return outputs
}

Select陷阱

阻塞在nil channel

Go
var ch chan int  // nil

select {
case v := <-ch:  // 永久阻塞
    // 不会执行
}

nil channel在select中永久阻塞,可用于禁用case。

关闭后的发送

Go
ch := make(chan int)
close(ch)

ch <- 1  // panic: send on closed channel

多次关闭

Go
close(ch)
close(ch)  // panic: close of closed channel

同步场景对比

场景推荐方式说明
单次同步无缓冲channel等待完成
数据传递有缓冲channel异步传递
多通道选择select多路复用
超时控制select + time.After避免阻塞
退出信号close channel广播退出
动态禁用nil channelselect不执行

要点总结

  • 无缓冲channel同步强,有缓冲异步传递
  • select多路复用多个channel
  • default实现非阻塞操作
  • time.After实现超时控制
  • 只有发送者关闭channel
  • range遍历自动检测关闭
  • nil channel在select中阻塞(可禁用case)
  • close后发送panic,多次close panic
  • close向所有接收者广播退出信号

📝 发现内容有误?点击此处直接编辑

← 上一篇 Go项目结构与代码组织
下一篇 → Go Context上下文控制详解
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库