gpt4 book ai didi

go - Race 暂停一组 goroutines

转载 作者:IT王子 更新时间:2023-10-29 01:34:46 28 4
gpt4 key购买 nike

我有一堆 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.resumeunsafe.Pointer并访问 chan struct{}atomic.LoadPointerloopatomic.StorePointerwhilePaused , 比赛结束了。这似乎提供了与 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/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com