Go routine阻塞问题:仅一个协程运行,另一个僵死/休眠求助
问题:双Go协程运行异常,一个僵死休眠
我有两个调用同一函数但参数不同的Go协程,程序运行时仅一个协程正常工作,另一个因未知原因处于僵死/休眠状态。我使用了两个互斥锁:mutexSearch用于busquedaBid函数,mutexCompare用于checkEqualityValues函数。但发现一个协程获取锁并执行解锁后,仍未释放代码块,导致另一个协程无法运行。输出显示其中一个队列长度始终为1,说明该协程未执行任务。
代码示例:
package main import ( "fmt" "sync" "time" ) var mutexCompare sync.Mutex type Cola struct { tomarLista int contadorSoluciones int cola [][]int } func main() { in := time.Now() puzzleInicial := []int{1,2,3,4,5,6,7,8,0} grafo := make(map[int][]int) grafo[0] = []int{1, 3} grafo[1] = []int{0, 2, 4} grafo[2] = []int{1, 5} grafo[3] = []int{0, 4, 6} grafo[4] = []int{1, 3, 5, 7} grafo[5] = []int{2, 4, 8} grafo[6] = []int{3, 7} grafo[7] = []int{4, 6, 8} grafo[8] = []int{5, 7} puzzleFinal := []int{1, 3, 6, 5, 2, 0, 4, 7, 8} colaInicial := [][]int{} colaFinal := [][]int{} colaInicial = append(colaInicial, puzzleInicial) colaFinal = append(colaFinal, puzzleFinal) tomarListaInicial := 0 contadorSolucionesInicial := 0 mutexSearch := &sync.Mutex{} done := make(chan bool) colaI := Cola{tomarListaInicial, contadorSolucionesInicial, colaInicial} colaF := Cola{tomarListaInicial, contadorSolucionesInicial, colaFinal} go busquedaBid(&colaI, &colaF, grafo, mutexSearch, done) go busquedaBid(&colaF, &colaI, grafo, mutexSearch, done) <-done f := time.Since(in) fmt.Println(f) } func busquedaBid(colaActual *Cola, colaCheck *Cola, grafo map[int][]int, mut *sync.Mutex, done chan bool) { for { mut.Lock() nodo := getIndex(colaActual.cola[colaActual.tomarLista]) for _, neighbour := range grafo[nodo] { sliceAux := append([]int(nil), colaActual.cola[colaActual.tomarLista]...) valorAux := sliceAux[neighbour] indCero := getIndex(sliceAux) sliceAux[neighbour] = 0 sliceAux[indCero] = valorAux if !checkRepetitionsSlices(colaActual.cola, sliceAux) { colaActual.cola = append(colaActual.cola, sliceAux) } } colaActual.tomarLista += 1 mut.Unlock() if checkEqualityValues(colaActual.cola, colaCheck.cola, done) { break } } } func checkEqualityValues(cola_inicial [][]int, cola_final [][]int, done chan bool) bool { mutexCompare.Lock() for _, item1 := range cola_inicial { for _, item2 := range cola_final { if checkRepetitionsSliceSlice(item1, item2) { fmt.Println(len(cola_inicial), len(cola_final)) close(done) return true } } } mutexCompare.Unlock() return false } // 补充用户未提供的依赖函数实现 func getIndex(slice []int) int { for i, v := range slice { if v == 0 { return i } } return -1 } func checkRepetitionsSlices(colas [][]int, slice []int) bool { for _, c := range colas { if checkRepetitionsSliceSlice(c, slice) { return true } } return false } func checkRepetitionsSliceSlice(a, b []int) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true }
解决方法
1. 修复checkEqualityValues的锁泄漏问题
当前函数在找到匹配项时直接return true,未释放mutexCompare,导致其他协程调用该函数时永久阻塞。添加defer mutexCompare.Unlock()确保无论函数如何退出都会释放锁:
func checkEqualityValues(cola_inicial [][]int, cola_final [][]int, done chan bool) bool { mutexCompare.Lock() defer mutexCompare.Unlock() // 确保锁一定会被释放 for _, item1 := range cola_inicial { for _, item2 := range cola_final { if checkRepetitionsSliceSlice(item1, item2) { fmt.Println(len(cola_inicial), len(cola_final)) close(done) return true } } } return false }
2. 移除不必要的mutexSearch互斥锁
两个协程操作的是独立的Cola实例(colaI和colaF),它们的cola和tomarLista字段没有共享,因此不需要用同一个互斥锁保护。移除mutexSearch后,两个协程可以并行执行:
func busquedaBid(colaActual *Cola, colaCheck *Cola, grafo map[int][]int, done chan bool) { for { nodo := getIndex(colaActual.cola[colaActual.tomarLista]) for _, neighbour := range grafo[nodo] { sliceAux := append([]int(nil), colaActual.cola[colaActual.tomarLista]...) valorAux := sliceAux[neighbour] indCero := getIndex(sliceAux) sliceAux[neighbour] = 0 sliceAux[indCero] = valorAux if !checkRepetitionsSlices(colaActual.cola, sliceAux) { colaActual.cola = append(colaActual.cola, sliceAux) } } colaActual.tomarLista += 1 if checkEqualityValues(colaActual.cola, colaCheck.cola, done) { break } // 监听done通道,及时退出无效循环 select { case <-done: return default: } } } // main函数中移除mutexSearch相关代码 func main() { // ... 其他代码不变 ... done := make(chan bool) colaI := Cola{tomarListaInicial, contadorSolucionesInicial, colaInicial} colaF := Cola{tomarListaInicial, contadorSolucionesInicial, colaFinal} go busquedaBid(&colaI, &colaF, grafo, done) go busquedaBid(&colaF, &colaI, grafo, done) <-done // ... 其他代码不变 ... }
3. 优化协程退出逻辑
在busquedaBid的循环中添加select分支监听done通道,当通道关闭时直接退出,避免找到解后继续执行无效循环。
内容的提问来源于stack exchange,提问作者leiro77
相关产品推荐
相关产品推荐

