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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 06:17:02