为什么所有的 goroutines 都在睡觉 - 僵局。识别瓶颈
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("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()
}
}
}
抛出的错误是。
致命错误:所有 goroutine 都在休眠 - 死锁!
goroutine 1 [semacquire]:
同步。(*等待组)。等待(0xc082004600)
c:/go/src/sync/waitgroup.go:132 +0x170
main.main()
E:/Go/go_projects/go/src/Test.go:22 +0x21a
我刚刚尝试了它 (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 使用。
如前所述,不要按值传递同步包中的类型,就在 sync
package documentation 顶部附近:“包含此包中定义的类型的值不应已复制。" 这也包括类型本身(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("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... ")
}
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()
}
}
}
抛出的错误是。
致命错误:所有 goroutine 都在休眠 - 死锁!
goroutine 1 [semacquire]: 同步。(*等待组)。等待(0xc082004600) c:/go/src/sync/waitgroup.go:132 +0x170 main.main() E:/Go/go_projects/go/src/Test.go:22 +0x21a
我刚刚尝试了它 (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 使用。
如前所述,不要按值传递同步包中的类型,就在 sync
package documentation 顶部附近:“包含此包中定义的类型的值不应已复制。" 这也包括类型本身(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("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... ")
}