比赛暂停一组goroutines

时间:2018-05-09 02:30:53

标签: go synchronization channel

我有一堆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)
}

我启动了所有正在运行loop()的32个goroutine,然后连续100次调用whilePaused,一切似乎都有效......但如果我用-race运行它,它会告诉我在l.resumewhilePaused)中写入并在l.resume = make(chan struct{})loop)中阅读时,<-l.resume上有一场竞赛。

我不明白为什么会这样。根据{{​​3}},close(l.pause)应该在每个<-l.pause goroutine loop之前发生。这应该意味着make(chan struct{})值在所有l.resume goroutine中显示为loop的值,就像字符"hello world"作为{的值可见一样{1}}文档示例中的a goroutine。{/}

可能相关的一些其他信息:

  • 如果我将f替换为l.resume,并使用unsafe.Pointer中的chan struct{}atomic.LoadPointer loop访问atomic.StorePointer值{1}},比赛消失了。这似乎提供了通道已经提供的完全相同的获取 - 释放顺序?

  • 如果我在whilePausedtime.Sleep(10 * time.Microsecond)之间添加l.paused.Done(),程序通常会在调用<-l.resume一两次后死锁。

    < / LI>
  • 如果我添加fn,程序将打印28 fmt.Printf(".") s,调用第一个函数,打印另外32个.,然后挂起(或偶尔,调用第二个函数,然后打印另外32个.并挂起)。

以下是我的其余代码,以防您想要运行整个代码:

.

1 个答案:

答案 0 :(得分:4)

This is because after the thread does l.paused.Done(), the other thread is able to go around the loop and assign l.resume again

Here is sequence of operations

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!!