
当使用for-range遍历go中的无缓冲或带缓冲但未关闭的通道时,循环会持续等待新数据,直到通道被显式关闭;若未关闭,程序将永久阻塞,无法执行后续逻辑。
当使用for-range遍历go中的无缓冲或带缓冲但未关闭的通道时,循环会持续等待新数据,直到通道被显式关闭;若未关闭,程序将永久阻塞,无法执行后续逻辑。
在Go并发编程中,`for range ch` 是读取通道的常见写法,但它隐含一个关键前提:**该通道最终必须被关闭**,否则循环永远不会退出。你的程序中创建了一个带缓冲的通道 `ch := make(chan *HttpResponse, len(urls))`,并启动了三个 goroutine 向其中发送结果,但从未调用 `close(ch)`。因此,`for i := range ch` 会在接收完全部3个值后继续阻塞,等待第4个(不存在的)值,导致 `"Im done"` 永远不会打印。正确的做法是:明确知道要接收的数据总量时,优先使用计数循环而非 range。修改 main 函数如下:
func main() {
fmt.Println("start")
ch := make(chan *HttpResponse, len(urls))
for _, url := range urls {
go asyncHttpGets(url, ch)
}
// 显式接收 len(urls) 个结果,避免阻塞
for i := 0; i <p>⚠️ 注意事项:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/learn/7564" title="使用Go语言搭建家庭相册系统-相关课件"><img
src="https://img.php.cn/upload/webcode/000/000/164/636a2b4d84031727.png" alt="使用Go语言搭建家庭相册系统-相关课件" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/learn/7564" title="使用Go语言搭建家庭相册系统-相关课件" class="overflowclass">使用Go语言搭建家庭相册系统-相关课件</a>
<p class="overflowclass">使用Go语言搭建家庭相册系统-相关课件</p>
</div>
<a rel="nofollow" href="/xiazai/learn/7564" title="使用Go语言搭建家庭相册系统-相关课件" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- for range ch 仅适用于生产者明确关闭通道的场景(例如:所有 goroutine 完成后由主协程或某一方调用 close(ch));
- 若使用 range,务必确保有且仅有一个 goroutine 负责关闭通道(通常在所有发送完成后),否则可能引发 panic(重复关闭)或死锁;
- 带缓冲通道(如本例 make(chan T, 3))虽能暂存数据,但不改变 range 的语义——它仍等待“通道关闭信号”,而非“缓冲区为空”。
补充建议:为提升健壮性,可为 HTTP 请求添加超时控制,并在 asyncHttpGets 中处理 err != nil 情况(如 Google 模拟超时失败时,resp 为 nil,应避免解引用)。但核心阻塞问题的根本解法,始终是避免对未关闭通道使用 for range。










