英文:
Why is this Go code deadlocking?
问题
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")
}
英文:
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")
}
I'm very new to Go, so any pointers would be great. How would I go about debugging such a thing?
You can run the snippet http://play.golang.org/p/SCr8TZXQUE
update: this works without the line <-time.After(time.Duration(id)*time.Millisecond)
on playground, I want to know why? (As mentioned by @dystroy this maybe because of the way playground handles time)
When I try locally, this is the output:
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
All the goroutines complete but throws a deadlock anyway. I should note that it doesn't matter if the timer is used, deadlocks either way.
答案1
得分: 7
从Goexit的文档中:
> Goexit终止调用它的goroutine。其他goroutine不受影响。Goexit在终止goroutine之前运行所有延迟调用。
你正在退出主例程。不要这样做。因为在你最后一个使用go check(i)
启动的例程完成之后,没有任何例程在运行,所以会出现“死锁”。只需删除这行代码:
defer runtime.Goexit()
如果你想在主例程中等待一组goroutine完成,你可以使用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可能会退出一个在标准程序中甚至不存在的例程。
英文:
From 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.
You're exiting the main routine. Don't. As you do it, there isn't any routine running after the last one you launched with go check(i)
has finished, hence the "deadlock". Simply remove this line :
defer runtime.Goexit()
If what you want is to wait in main for a group of goroutines to finish, you may use a 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")
}
EDIT :
if you're testing it on golang's playground, any time.After
will deadlock because time is frozen in playground and Goexit maybe exit a routine that doesn't even exist in a standard program.
答案2
得分: 0
all your goroutines are waiting for someone to consume the value they are sending in <-time.After
.
you can just delete the <-
or make main consume the value of all goroutines you launched.
Edit
this worked for me
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")
}
witch is the same solution someone proposed before so I'll propose a solution without a waitgroup
package main
import "fmt"
import "time"
func check(id int, c chan bool) {
fmt.Println("Checked", id)
time.After(time.Duration(id)*time.Millisecond)
fmt.Println("Woke up", id)
c <- true
}
func main() {
c := make(chan bool)
for i := 0; i <= 10; i++ {
fmt.Println("Called with", i)
go check(i, c)
}
var n uint
for n<10 {
<- c
n++
}
fmt.Println("Done for")
}
英文:
all your goroutines are waiting for someone to consume the value they are sending in <-time.After
.
you can just delete the <-
or make main consume the value of all goroutines you launched.
Edit
this worked for me
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")
}
witch is the same solution someone proposed before so I'll propose a solution without a waitgroup
package main
import "fmt"
import "time"
func check(id int, c chan bool) {
fmt.Println("Checked", id)
time.After(time.Duration(id)*time.Millisecond)
fmt.Println("Woke up", id)
c <- true
}
func main() {
c := make(chan bool)
for i := 0; i <= 10; i++ {
fmt.Println("Called with", i)
go check(i, c)
}
var n uint
for n<10 {
<- c
n++
}
fmt.Println("Done for")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论