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个切片,直接发送完全不会阻塞,这个非阻塞逻辑反而容易让你忽略真正的问题。
修复方案
咱们只需要做两个关键修改,就能解决死锁:
- 发送完所有数据后关闭中间channel:在遍历完map的所有值之后,调用
close(ic),这样for range遍历到最后就会自动退出循环。 - 简化不必要的非阻塞逻辑:直接发送数据到
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("程序执行完成") }
修复后的执行流程
f1遍历map,把所有切片发送到ic,然后关闭ic。for range ic遍历所有发送过来的切片,合并成一个大切片strs。f1把strs发送到c,然后给q发送完成信号。f3从c收到切片并打印,main函数收到q的信号后结束程序。
这样整个流程就完全顺畅,不会再出现死锁啦!
内容的提问来源于stack exchange,提问作者Pritam
相关产品推荐
相关产品推荐

