Golang程序启用race标志后未返回的问题

Golang程序启用race标志后未返回的问题 各位专家好,

我正在尝试创建一个死锁错误,并使用了以下代码:

package main

import "fmt"

func main() {
	channel := make(chan int)
	go func(channel chan int) {
		for i := 0; i < 100; i++ {
			channel <- i
		}
	}(channel)

	for val := range channel {
		fmt.Println(val)
	}
}

当我使用以下命令运行程序时

go run -race main.go

程序永远不会返回。但是,当我运行相同的程序而不使用 -race 标志时,它会返回一个死锁错误。

请帮我解决这个问题。


更多关于Golang程序启用race标志后未返回的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

大家好,

这真的是一个死锁吗?他只是从一个永远不会关闭的通道中读取数据,我认为这种行为既是正常的,也是符合设计预期的。

更多关于Golang程序启用race标志后未返回的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


@telo_tade 如果我说错了请纠正我。一般来说,死锁就是“等待永远不会发生的事情”。在这个例子中,消费者会等待从通道传来值,但这永远不会发生,并且没有其他 goroutine 可以执行,因此会抛出死锁错误。是这样吗?

你好 @yhgupta

我认为这关系不大,但维基百科的定义谈到了 Coffman 条件:死锁 - 维基百科

显然,在你的示例中并没有发生这4个条件。

这是一个典型的死锁检测问题。当使用 -race 标志时,Go 的竞态检测器会改变程序的调度行为,这可能导致死锁检测的时机发生变化。

在你的代码中,问题在于:

  1. 协程发送了 100 个值到 channel
  2. 主协程通过 range 循环接收这些值
  3. 发送协程完成后,channel 没有被关闭,导致主协程在 range 上永久阻塞

解决方案: 在发送完成后关闭 channel:

package main

import "fmt"

func main() {
    channel := make(chan int)
    go func(channel chan int) {
        for i := 0; i < 100; i++ {
            channel <- i
        }
        close(channel) // 关键:发送完成后关闭channel
    }(channel)

    for val := range channel {
        fmt.Println(val)
    }
}

或者使用带缓冲的 channel:

package main

import "fmt"

func main() {
    channel := make(chan int, 100) // 使用缓冲channel
    go func(channel chan int) {
        for i := 0; i < 100; i++ {
            channel <- i
        }
    }(channel)

    // 等待所有值发送完成
    for i := 0; i < 100; i++ {
        val := <-channel
        fmt.Println(val)
    }
}

关于竞态检测器行为的说明:

竞态检测器 (-race) 会:

  1. 增加内存使用和运行时间
  2. 改变 goroutine 的调度时机
  3. 可能延迟死锁检测

在不使用 -race 时,Go 运行时能更快检测到所有 goroutine 都阻塞的情况。使用 -race 时,由于额外的同步和检查,死锁检测可能不会立即触发。

验证死锁的更好方法:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    
    // 第一次加锁
    mu.Lock()
    
    // 尝试在同一个goroutine中再次加锁(会导致死锁)
    mu.Lock() // 这里会永久阻塞
    
    fmt.Println("这行永远不会执行")
}

这个例子在使用和不使用 -race 时都会产生死锁。

回到顶部