英文:
Why all goroutines are asleep - deadlock. Identifying bottleneck
问题
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
intInputChan := make(chan int, 50)
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(intInputChan, wg)
}
for i := 1; i < 51; i++ {
fmt.Printf("输入:%d \n", i)
intInputChan <- i
}
close(intInputChan)
wg.Wait()
fmt.Println("主应用程序退出...")
panic("---------------")
}
func worker(input chan int, wg sync.WaitGroup) {
defer func() {
fmt.Println("执行延迟函数...")
wg.Done()
}()
for {
select {
case intVal, ok := <-input:
time.Sleep(100 * time.Millisecond)
if !ok {
input = nil
return
}
fmt.Printf("%d %v\n", intVal, ok)
default:
runtime.Gosched()
}
}
}
抛出的错误是:
致命错误:所有的goroutine都处于休眠状态 - 死锁!
goroutine 1 [semacquire]:
sync.(*WaitGroup).Wait(0xc082004600)
c:/go/src/sync/waitgroup.go:132 +0x170
main.main()
E:/Go/go_projects/go/src/Test.go:22 +0x21a
英文:
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
intInputChan := make(chan int, 50)
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(intInputChan, wg)
}
for i := 1; i < 51; i++ {
fmt.Printf("Inputs. %d \n", i)
intInputChan <- i
}
close(intInputChan)
wg.Wait()
fmt.Println("Existing Main App... ")
panic("---------------")
}
func worker(input chan int, wg sync.WaitGroup) {
defer func() {
fmt.Println("Executing defer..")
wg.Done()
}()
for {
select {
case intVal, ok := <-input:
time.Sleep(100 * time.Millisecond)
if !ok {
input = nil
return
}
fmt.Printf("%d %v\n", intVal, ok)
default:
runtime.Gosched()
}
}
}
error thrown is.
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.(*WaitGroup).Wait(0xc082004600)
c:/go/src/sync/waitgroup.go:132 +0x170
main.main()
E:/Go/go_projects/go/src/Test.go:22 +0x21a
答案1
得分: 8
我刚刚尝试了一下(playground),传递了一个wg *sync.WaitGroup
,它可以工作。
传递sync.WaitGroup
意味着传递了sync.WaitGroup
的副本(按值传递):goroutine将Done()
方法应用于不同的sync.WaitGroup
。
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(intInputChan, &wg)
}
注意&wg
:你通过值传递了指向原始sync.WaitGroup
的指针,供goroutine使用。
英文:
I just tried it (playground) passing a wg *sync.WaitGroup
and it works.
Passing sync.WaitGroup
means passing a copy of the sync.WaitGroup
(passing by value): the goroutine mentions Done()
to a different sync.WaitGroup
.
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(intInputChan, &wg)
}
Note the &wg
: you are passing by value the pointer to the original sync.WaitGroup
, for the goroutine to use.
答案2
得分: 3
如上所述,在sync
包文档的开头附近提到,不要通过值传递来传递sync
包中的类型:"不应该复制包含此包中定义的类型的值",这也包括类型本身(sync.Mutex
,sync.WaitGroup
等)。
然而,有几点需要注意:
- 如果你知道要添加多少个,可以只调用一次
wg.Add
(但要确保在任何地方调用Wait
之前完成)。 - 你不应该像那样调用
runtime.Gosched
,它会使工作线程忙于循环。 - 你可以使用
range
从通道中读取以简化在通道关闭时停止。 - 对于小函数,你可以使用闭包而不必传递通道或等待组。
代码如下:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
const numWorkers = 3
c := make(chan int, 10)
var wg sync.WaitGroup
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go func() {
defer func() {
fmt.Println("执行延迟操作...")
wg.Done()
}()
for v := range c {
fmt.Println("接收:", v)
time.Sleep(100 * time.Millisecond)
}
}()
}
for i := 1; i < 51; i++ {
fmt.Println("发送:", i)
c <- i
}
fmt.Println("关闭通道...")
close(c)
fmt.Println("等待...")
wg.Wait()
fmt.Println("退出主应用程序...")
}
英文:
As mentioned, don't pass types from the sync package around by value, right near the top of the sync
package documentation: "Values containing the types defined in this package should not be copied." That also includes the types themselves (sync.Mutex
, sync.WaitGroup
, etc).
However, several notes:
- You can use just a single call to
wg.Add
if you know how many you're going to add (but as documented make sure it's done before anything can callWait
). - You don't want to call
runtime.Gosched
like that; it makes the workers busy loop. - You can use
range
to read from the channel to simplify stopping when it's closed. - For small functions you can use a closure and not bother to pass the channel or wait group at all.
That turns it into this:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
const numWorkers = 3
c := make(chan int, 10)
var wg sync.WaitGroup
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go func() {
defer func() {
fmt.Println("Executing defer…")
wg.Done()
}()
for v := range c {
fmt.Println("recv:", v)
time.Sleep(100 * time.Millisecond)
}
}()
}
for i := 1; i < 51; i++ {
fmt.Println("send:", i)
c <- i
}
fmt.Println("closing…")
close(c)
fmt.Println("waiting…")
wg.Wait()
fmt.Println("Exiting Main App... ")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论