concurrency - 为什么这段 Go 代码会死锁?

标签 concurrency go deadlock goroutine

package main

import "fmt"
import "runtime"
import "time"


func check(id int) {
    fmt.Println("Checked", id)
    <-time.After(time.Duration(id)*time.Millisecond)
    fmt.Println("Woke up", id)
}

func main() {
    defer runtime.Goexit()

    for i := 0; i <= 10; i++ {
        fmt.Println("Called with", i)
        go check(i)
    }

    fmt.Println("Done for")
}

我是 Go 的新手,所以任何指示都会很棒。我将如何调试这样的东西?

您可以运行代码片段 http://play.golang.org/p/SCr8TZXQUE

更新:没有 <-time.After(time.Duration(id)*time.Millisecond) 这行也能工作在 Playground 上,我想知道为什么? (正如@dystroy 所提到的,这可能是因为 Playground 处理时间的方式)

当我在本地尝试时,这是输出:

Called with  0
Called with  1
Checked 0
Called with  2
Checked 1
Called with  3
Checked 2
Called with  4
Woke up 0
Checked 3
Called with  5
Checked 4
Called with  6
Checked 5
Called with  7
Checked 6
Called with  8
Checked 7
Called with  9
Checked 8
Called with  10
Checked 9
Woke up 1
Done for
Checked 10
Woke up 2
Woke up 3
Woke up 4
Woke up 5
Woke up 6
Woke up 7
Woke up 8
Woke up 9
Woke up 10
throw: all goroutines are asleep - deadlock!

goroutine 2 [syscall]:
created by runtime.main
    /tmp/bindist046461602/go/src/pkg/runtime/proc.c:221

goroutine 5 [timer goroutine (idle)]:
created by addtimer
    /tmp/bindist046461602/go/src/pkg/runtime/ztime_amd64.c:69
exit status 2

所有的 goroutines 都完成了,但无论如何都会抛出一个死锁。我应该注意,是否使用计时器并不重要,无论哪种方式都会发生死锁。

最佳答案

来自 the documentation of Goexit :

Goexit terminates the goroutine that calls it. No other goroutine is affected. Goexit runs all deferred calls before terminating the goroutine.

您正在退出主例程。不。当您执行此操作时,在您使用 go check(i) 启动的最后一个例程完成后,没有任何例程运行,因此出现“死锁”。只需删除这一行:

defer runtime.Goexit()

如果你想要在 main 中等待一组 goroutines 完成,你可以使用 sync.WaitGroup :

package main

import (
    "fmt"
    "sync"
    "time"
)

func check(id int, wg *sync.WaitGroup) {
    fmt.Println("Checked", id)
    <-time.After(time.Duration(id)*time.Millisecond)
    fmt.Println("Woke up", id)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i <= 10; i++ {
        wg.Add(1)
        fmt.Println("Called with", i)
        go check(i, &wg)
    }
    wg.Wait()
    fmt.Println("Done for")
}

编辑:

如果你在 golang 的 Playground 上测试它,任何 time.After 都会死锁,因为时间在 Playground 上被卡住并且 Goexit 可能退出标准程序中甚至不存在的例程。

关于concurrency - 为什么这段 Go 代码会死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12745771/

相关文章:

go - 使用 golang 的四叉树递归并发

java - 使用 Java 8 Arrays.parallelSetAll() 的正确方法是什么?

java - 在并发包中惯用地使用 ReentrantLock

linux - 为什么这个 C++11 程序不会死锁?

Golang 可见性或 CPU 线程缓存问题

pointers - 指向方法参数中接口(interface)的指针?

go - 在 Go 中作为命令启动 screen ?

go - 在关联表中插入时创建关联问题

java - LIBGDX,安卓 : deadlock killing app when when returning captured picture

java - 静态 block 中thread.join()引起的死锁