You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中Select添加Default分支为何触发死锁?

为什么Go语言Select语句添加Default分支会触发死锁?

问题重现

当在接收通道的Select语句中添加default分支时,代码运行会触发死锁:

package main

import (
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    a := make(chan int)

    go func() {
        defer wg.Done()
        for {
            var (
                name string
                x    int
                ok   bool
            )

            select {
            case x, ok = <-a:
                name = "a"
            default:
                println("default error")
            }

            if !ok {
                return
            }

            println(name, x, ok)
        }

    }()
    go func() {
        defer wg.Done()
        defer close(a)

        for i := 0; i < 10; i++ {
            select {
            case a <- i:
            }
        }
    }()

    wg.Wait()
}

运行输出:

a 0 true
default error
fatal error: all goroutines are asleep - deadlock!

移除default分支后,代码运行完全正常:

package main

import (
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    a := make(chan int)

    go func() {
        defer wg.Done()
        for {
            var (
                name string
                x    int
                ok   bool
            )

            select {
            case x, ok = <-a:
                name = "a"
            }

            if !ok {
                return
            }

            println(name, x, ok)
        }

    }()
    go func() {
        defer wg.Done()
        defer close(a)

        for i := 0; i < 10; i++ {
            select {
            case a <- i:
            }
        }
    }()

    wg.Wait()
}

运行输出:

a 0 true
a 1 true
a 2 true
a 3 true
a 4 true
a 5 true
a 6 true
a 7 true
a 8 true
a 9 true

死锁原因分析

  • 无缓冲通道的核心特性:代码中创建的a是无缓冲通道,这类通道的发送和接收操作必须配对执行——发送方会阻塞直到有接收方准备好接收数据,接收方也会阻塞直到通道中有数据可接收。
  • 带default的Select行为:当Select语句包含default分支时,如果所有case对应的通道操作都无法立即执行,会立刻执行default分支,不会阻塞等待。
  • 死锁的具体触发流程:
    1. 发送方发送第一个值0,接收方正常接收并打印,此时通道回到空状态。
    2. 接收方再次进入循环,Select检测到通道无数据可接收,直接执行default分支打印"default error",由于ok仍然是true(没有接收到通道关闭的信号),循环会继续执行。
    3. 此时发送方尝试发送下一个值1,但无缓冲通道需要接收方处于等待接收的状态才能完成发送,而接收方一直在循环执行default分支,根本没有在等待接收数据,发送方因此陷入阻塞。
    4. 接收方不断重复执行default分支,永远不会退出循环;发送方阻塞在发送操作上;主goroutine阻塞在wg.Wait()上。所有goroutine都进入无法推进的阻塞状态,最终触发死锁。
  • 移除default后的正常逻辑:接收方的Select没有default分支,会一直阻塞在case x, ok = <-a:上等待数据。发送方发送数据时,接收方会被唤醒处理数据,发送完成后继续下一次发送,直到所有10个值发送完毕,发送方关闭通道。接收方收到通道关闭的信号(ok=false)后退出循环,所有goroutine正常结束,不会触发死锁。

内容的提问来源于stack exchange,提问作者Syuan-Cheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 03:35:27