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

在Go语言中不使用channel是否可能发生死锁?

Can deadlocks occur in Go without using channels?

Absolutely! Channels are just one common synchronization mechanism that can lead to deadlocks in Go, but deadlocks boil down to circular waits and unresolvable resource contention—you can hit them with other sync primitives, or even with plain goroutines waiting on each other without any explicit tools.

Let’s walk through a few concrete examples:

1. Deadlock with nested mutex locks

This is the classic "circular wait" scenario using sync.Mutex: two goroutines each hold one lock, then wait for the other lock that the other goroutine is holding. Neither can proceed.

package main

import (
    "sync"
)

func main() {
    var mutexA, mutexB sync.Mutex

    go func() {
        mutexA.Lock()
        defer mutexA.Unlock()
        
        // Give the other goroutine time to grab mutexB first
        mutexB.Lock()
        defer mutexB.Unlock()
        println("Goroutine 1 finished")
    }()

    go func() {
        mutexB.Lock()
        defer mutexB.Unlock()
        
        // Give the first goroutine time to grab mutexA first
        mutexA.Lock()
        defer mutexA.Unlock()
        println("Goroutine 2 finished")
    }()

    // Block the main goroutine to prevent program exit
    select {}
}

When you run this, both goroutines will hang forever—each holds one lock and waits for the other, creating an unbreakable cycle.

2. Deadlock with misused sync.WaitGroup

Another common case is misusing sync.WaitGroup where goroutines end up waiting on the same group that depends on them to complete:

package main

import "sync"

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        // This goroutine waits for the WaitGroup to complete...
        wg.Wait()
        println("Goroutine 1 done")
    }()

    go func() {
        defer wg.Done()
        // ...and so does this one
        wg.Wait()
        println("Goroutine 2 done")
    }()

    // The main goroutine also waits for the WaitGroup
    wg.Wait()
}

Here, the main goroutine waits for the two child goroutines to call Done(), but each child goroutine is waiting for the WaitGroup to be marked as complete. None of them can make progress—deadlock.

3. Plain goroutines in a circular wait

Even without any sync libraries, you can create a deadlock if goroutines are stuck waiting for each other to trigger a state change:

package main

var ready1, ready2 bool

func main() {
    go func() {
        // Wait for goroutine 2 to signal it's ready
        for !ready2 {
            // Spin-wait (no progress made)
        }
        ready1 = true
        println("Goroutine 1 finished")
    }()

    go func() {
        // Wait for goroutine 1 to signal it's ready
        for !ready1 {
            // Spin-wait (no progress made)
        }
        ready2 = true
        println("Goroutine 2 finished")
    }()

    // Keep main goroutine alive
    select {}
}

Both goroutines are stuck in infinite loops waiting for the other to set a flag. Neither will ever proceed, resulting in a deadlock (or a form of livelock, but from the program's perspective, it's effectively stuck forever).

The core of deadlocks

Remember, deadlocks happen when four conditions are met:

  • Mutual exclusion: Resources can't be shared (e.g., a mutex lock is held by only one goroutine at a time)
  • Hold and wait: A goroutine holds a resource while waiting for another
  • No preemption: Resources can't be taken away from a goroutine forcefully
  • Circular wait: A cycle of goroutines each waiting for a resource held by the next in the cycle

Channels are just one way to create these conditions, but they’re far from the only way. Any code that creates this circular dependency will deadlock, regardless of whether channels are involved.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 15:32:27