我有一堆 goroutines 在循环中做一些事情。我希望能够暂停所有这些,运行一些任意代码,然后恢复它们。我尝试这样做的方式可能不是惯用的(我希望有更好的解决方案),但我不明白为什么它不起作用。
精简到最基本的部分(底部的驱动程序代码):
type looper struct {
pause chan struct{}
paused sync.WaitGroup
resume chan struct{}
}
func (l *looper) loop() {
for {
select {
case <-l.pause:
l.paused.Done()
<-l.resume
default:
dostuff()
}
}
}
func (l *looper) whilePaused(fn func()) {
l.paused.Add(32)
l.resume = make(chan struct{})
close(l.pause)
l.paused.Wait()
fn()
l.pause = make(chan struct{})
close(l.resume)
}
我启动了 32 个 goroutines 都在运行 loop()
, 然后调用 whilePaused
连续 100 次,似乎一切正常……但如果我用 -race
运行它,它告诉我在 l.resume
上有一场比赛在 whilePaused
中写入它之间( l.resume = make(chan struct{})
) 并在 loop
中阅读(<-l.resume
)。
我不明白为什么会这样。根据The Go Memory Model , 那close(l.pause)
应该在 <-l.pause
之前发生在每个loop
协程。这应该意味着 make(chan struct{})
值作为 l.resume
的值可见在所有这些loop
goroutines,以同样的方式字符串 "hello world"
作为 a
的值可见在f
文档示例中的 goroutine。
一些可能相关的附加信息:
如果我替换
l.resume
用unsafe.Pointer
并访问chan struct{}
值atomic.LoadPointer
在loop
和atomic.StorePointer
在whilePaused
, 比赛结束了。这似乎提供了与 channel 应该提供的完全相同的获取-释放顺序?如果我添加
time.Sleep(10 * time.Microsecond)
在l.paused.Done()
之间和<-l.resume
, 程序通常在调用fn
后死锁一两次。如果我添加
fmt.Printf(".")
相反,程序打印 28.
s,调用第一个函数,打印另一个 32.
s,然后挂起(或者偶尔调用第二个函数,然后打印另一个 32.
s 并挂起)。
这是我的其余代码,如果你想运行整个代码:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
// looper code from above
var n int64
func dostuff() {
atomic.AddInt64(&n, 1)
}
func main() {
l := &looper{
pause: make(chan struct{}),
}
var init sync.WaitGroup
init.Add(32)
for i := 0; i < 32; i++ {
go func() {
init.Done()
l.loop()
}()
}
init.Wait()
for i := 0; i < 100; i++ {
l.whilePaused(func() { fmt.Printf("%d ", i) })
}
fmt.Printf("\n%d\n", atomic.LoadInt64(&n))
}
最佳答案
这是因为在线程执行 l.paused.Done() 之后,另一个线程能够绕过循环并再次分配 l.resume
这是操作顺序
Looper thread | Pauser thread
------------------------------------
l.paused.Done() |
| l.paused.Wait()
| l.pause = make(chan struct{})
| round the loop
| l.paused.Add(numThreads)
<- l.resume | l.resume = make(chan struct{}) !!!RACE!!
关于go - Race 暂停一组 goroutines,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50244690/