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

Go语言中通过goroutine合并多个slice时如何避免死锁?

解决Go中map转切片时的channel死锁问题

兄弟,你的代码死锁的核心原因其实很明确——中间channel ic没有被关闭!咱们来一步步拆解问题,再给你修复方案:

为什么会触发死锁?

你在f1里用for v := range ic遍历中间channel的时候,Go的规则是:这个循环会一直阻塞,直到channel被明确关闭,因为它默认认为可能还有新的数据会发过来。而你的代码里,把所有切片发送到ic之后,根本没关这个channel,所以这个循环会无限卡住,后面的c <- strs和q <-1完全执行不到。main函数里的<-q也一直等不到消息,整个程序就彻底死锁了。

另外提一句,你的f2函数用了非阻塞select发送数据到ic,其实没必要——ic的缓冲是10,而你的map里只有3个切片,直接发送完全不会阻塞,这个非阻塞逻辑反而容易让你忽略真正的问题。

修复方案

咱们只需要做两个关键修改,就能解决死锁:

  1. 发送完所有数据后关闭中间channel:在遍历完map的所有值之后,调用close(ic),这样for range遍历到最后就会自动退出循环。
  2. 简化不必要的非阻塞逻辑:直接发送数据到ic即可,不用多此一举的select。

下面是修改后的完整代码:

package main
import (
	"fmt"
)
func f1(c chan []string, q chan int) {
	// intermediate channel
	ic := make(chan []string, 10)
	hmap := map[int][]string{
		0: []string{"a", "b", "c"},
		1: []string{"d", "e"},
		2: []string{"f", "g", "h"},
	}
	// 直接发送切片到中间channel,不需要额外函数
	for _, v := range hmap {
		ic <- v
		fmt.Println("Sent to intermediate channel:", v)
	}
	// 关键:发送完所有数据后关闭中间channel
	close(ic)

	// 现在可以正常遍历中间channel的所有数据了
	var strs []string
	for v := range ic {
		strs = append(strs, v...)
	}
	fmt.Println("合并后的切片:", strs)

	// 发送合并后的切片到目标channel
	c <- strs
	fmt.Println("已将合并后的切片发送到目标channel")

	q <- 1
}

func f3(c chan []string) {
	fmt.Println("f3收到的切片:", <-c)
}

func main() {
	c := make(chan []string, 10)
	q := make(chan int)
	go f1(c, q)
	go f3(c)
	// 等待f1完成信号
	<-q
	fmt.Println("程序执行完成")
}

修复后的执行流程

  1. f1遍历map,把所有切片发送到ic,然后关闭ic。
  2. for range ic遍历所有发送过来的切片,合并成一个大切片strs。
  3. f1把strs发送到c,然后给q发送完成信号。
  4. f3从c收到切片并打印,main函数收到q的信号后结束程序。

这样整个流程就完全顺畅,不会再出现死锁啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:19:56