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

为何此案例中主goroutine会阻塞进而引发死锁?

Go代码死锁原因解析

问题描述

以下Go代码执行时会触发死锁,主goroutine被阻塞:

package main

import "fmt"

func square(numbers chan int, squares chan int) {
    for n := range numbers {
        squares <- n * n
    }
    close(squares)
}

func main() {
    numbers := make(chan int)
    squares := make(chan int)

    go square(numbers, squares)

    for i := 0; i < 10; i++ {
        numbers <- i
    }
    close(numbers)

    for s := range squares {
        fmt.Println(s)
    }
}

已知将向numbers通道发送数据的逻辑抽离到单独goroutine中可以解决问题,示例代码如下:

go func() {
    for i := 0; i < 10; i++ {
        numbers <- i
    }
    close(numbers)
}()

但存在疑问:原代码里,主goroutine第一次向numbers发送数据被阻塞后,调度器难道不会启动square goroutine,让两者来回通信吗?为什么会产生死锁?

死锁原因解析

核心问题出在无缓冲通道的阻塞特性和主goroutine的执行逻辑上:

  1. 你创建的numbers和squares都是无缓冲通道,无缓冲通道的发送操作必须等对应的接收操作完成才能继续,接收操作也必须等发送操作完成才能推进。
  2. 主goroutine启动square goroutine后,立刻进入循环向numbers发数据。此时square goroutine可能还没被调度器执行(Go调度器不保证goroutine的执行顺序),主goroutine执行numbers <- 0时,因为没有接收方,直接被阻塞。
  3. 就算square goroutine被调度起来,它从numbers拿到0后,会执行squares <- 0——但squares也是无缓冲通道,此时主goroutine还卡在numbers的发送操作上,根本没开始遍历squares接收数据,所以square goroutine也会被阻塞在squares的发送步骤上。
  4. 到这一步,两个goroutine彻底卡死:主goroutine等有人接numbers的数据,square goroutine等有人接squares的数据,谁都没法推进,最终触发死锁。

而把发送numbers的逻辑放到单独goroutine后:

  • 新goroutine负责发数据到numbers,主goroutine可以立刻开始处理squares的接收。
  • 当新goroutine发数据被阻塞时,主goroutine已经在接收squares的数据,square goroutine的发送操作能得到响应,三个goroutine形成正常的通信流转,不会出现互相堵死的情况。

内容的提问来源于stack exchange,提问作者Don Draper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:57:50