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

go run与go test执行含通道代码结果不同的原因咨询

Why go run deadlocks but go test hangs without error?

Great question! Let’s break down exactly why you’re seeing this weird discrepancy between the two commands, using your code as a reference.

First, let’s recap your code for clarity:

main.go

package main

import "fmt"

func main() {
	fmt.Println("hello")
	ch := make(chan struct{}, 1)
	<-ch
}

main_test.go

package main

import "testing"

func Test_Main(t *testing.T) {
	main()
}

What’s going on with go run main.go?

When you run go run, the Go runtime spins up exactly one goroutine: the main one. Here’s the play-by-play:

  1. It prints "hello" like expected.
  2. It creates a buffered channel (size 1) but never sends anything to it.
  3. It hits <-ch and tries to receive from the channel. Since there’s no data in the channel, and no other goroutines to send data to it, the main goroutine blocks permanently.

Go’s runtime has a built-in deadlock detector that checks if every single goroutine is asleep with no possible path forward. Since the only goroutine is stuck waiting on a channel with no sender, it triggers the familiar fatal error: all goroutines are asleep - deadlock! message and exits immediately.

Why does go test hang instead of deadlocking?

The go test command runs your test under the Go testing framework, which starts several background goroutines to manage the test lifecycle. These include:

  • Goroutines tracking test execution time
  • Goroutines handling output and result reporting
  • Framework goroutines waiting to clean up after the test finishes

When your Test_Main function calls main(), the test-specific goroutine blocks on <-ch — but crucially, the framework’s background goroutines are still active (or at least not permanently stuck). Go’s deadlock detector only fires when every goroutine in the program is stuck with no way to proceed. Since some framework goroutines are still alive (even if they’re just waiting for your test to finish), the runtime doesn’t flag this as a deadlock.

Your test will hang forever until you manually kill it, or if you set a timeout with the -timeout flag (e.g., go test -v -timeout 2s main_test.go -run=Test_Main), the framework will terminate it automatically once the timeout hits.

What this means for your project

If you’re using this channel pattern in production code, you need to guarantee there’s always a sender for the channel before you attempt to receive from it. If the sender might be missing or could fail, consider adding safeguards like:

  • A select statement with a timeout to avoid permanent blocking:
    import "time"
    
    // ...
    select {
    case <-ch:
        // Handle received value
    case <-time.After(5 * time.Second):
        fmt.Println("Timed out waiting for channel")
        // Handle timeout scenario
    }
    
  • A context to cancel the receive operation if needed:
    import "context"
    
    // ...
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    select {
    case <-ch:
        // Handle received value
    case <-ctx.Done():
        fmt.Println("Context canceled:", ctx.Err())
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 20:12:31