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

为何Go单main函数读写通道报错,协程方式却可行?

Go语言通道(Channel)运行报错原因解析

你遇到的报错是因为同一个goroutine内的通道读写逻辑导致了死锁,下面结合代码详细说明:

可正常运行的代码

package main

import (
    "fmt"
)

func inserChannel(c chan int) {
    for i := 0; i < cap(c); i++ {
        c <- i * i
    }
    defer close(c)
}

func main() {
    c := make(chan int, 10)
    go inserChannel(c)
    for i := range c {
        fmt.Println(i)
    }
}

报错的代码

package main

import (
    "fmt"
)

func main() {
    c := make(chan int, 10)
    for i := 0; i < cap(c); i++ {
        c <- i * i
    }
    defer close(c)
    for i := range c {
        fmt.Println(i)
    }
}

原因说明

第一种写法可行的核心

使用go inserChannel(c)启动了独立的goroutine执行写入操作,main goroutine可以同时进入for range读取通道。当写入goroutine完成所有元素写入后,defer close(c)会立即关闭通道——for range遍历通道时,会在通道关闭且所有元素读取完毕后自动退出,程序正常结束。

第二种写法报错的关键

你在同一个main goroutine里先完成了全部元素的写入(缓冲通道容量为10,写入10个元素不会阻塞),随后的defer close(c)是延迟到main函数退出时才执行的。当for range读完通道内的10个元素后,通道并未关闭,for range会一直阻塞等待新元素写入,而此时没有其他goroutine会往通道写数据,最终导致main goroutine死锁,触发报错。

写入逻辑作为普通函数调用可行的原因

如果把写入逻辑放到普通函数(不启动新goroutine),只要在写入完成后立即关闭通道(而非延迟到main退出),for range读完所有元素后就会因通道关闭而正常退出,不会阻塞。示例代码如下:

package main

import (
    "fmt"
)

func inserChannel(c chan int) {
    for i := 0; i < cap(c); i++ {
        c <- i * i
    }
    close(c) // 写入完成后直接关闭通道
}

func main() {
    c := make(chan int, 10)
    inserChannel(c) // 普通函数调用
    for i := range c {
        fmt.Println(i)
    }
}

内容的提问来源于stack exchange,提问作者Hari Baskar S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:17:04