doutang1992 2015-11-21 09:02
浏览 62
已采纳

sync.Cond测试广播-为什么要循环检查?

I was trying to use sync.Cond - Wait and Broadcast. I could not understand some parts of it:

The comment for Wait calls says:

   41    // Because c.L is not locked when Wait first resumes, the caller
   42    // typically cannot assume that the condition is true when
   43    // Wait returns.  Instead, the caller should Wait in a loop:
   44    //
   45    //    c.L.Lock()
   46    //    for !condition() {
   47    //        c.Wait()
   48    //    }
   49    //    ... make use of condition ...
   50    //    c.L.Unlock()

What is the reason this is required?

So this means the following program may not be correct (all though it works):

package main

import (
    "bufio"
    "fmt"
    "os"
    "sync"
)

type processor struct {
    n       int
    c       *sync.Cond
    started chan int
}

func newProcessor(n int) *processor {

    p := new(processor)
    p.n = n

    p.c = sync.NewCond(&sync.Mutex{})

    p.started = make(chan int, n)

    return p
}

func (p *processor) start() {

    for i := 0; i < p.n; i++ {
        go p.process(i)
    }

    for i := 0; i < p.n; i++ {
        <-p.started
    }
    p.c.L.Lock()
    p.c.Broadcast()
    p.c.L.Unlock()

}

func (p *processor) process(f int) {

    fmt.Printf("fork : %d
", f)

    p.c.L.Lock()
    p.started <- f
    p.c.Wait()
    p.c.L.Unlock()
    fmt.Printf("process: %d - out of wait
", f)
}

func main() {

    p := newProcessor(5)
    p.start()

    reader := bufio.NewReader(os.Stdin)
    _,_ =reader.ReadString('
')

}
  • 写回答

1条回答 默认 最新

  • duanaixuan7385 2015-11-21 17:33
    关注

    Condition variables doesn't stay signaled, they only wake up other go routines that are blocking in .Wait(). So this presents a race condition unless you have a predicate where you check if you even need to wait, or if the thing you want to wait for has already happened.

    In your particular case, you have added synchronization between the go routines calling .Wait() and the one calling .BroadCast() by using your p.started channel, in a manner that as far as I can tell should not present the race condition I'm describing further on in this post. Though I wouldn't bet on it, and personally I would just do it the idiomatic way like the documentation describes.

    Consider your start() function is executing the broadcast in these lines:

    p.c.L.Lock()
    p.c.Broadcast()
    

    At that specific point in time consider that one of your other go routines have come to this point in your process() function

    fmt.Printf("fork : %d
    ", f)
    

    The next thing that go routine will do is lock the mutex (which it isn't going to own at least until the go routine in start() releases that mutex) and wait on the condition variable.

    p.c.L.Lock()
    p.started <- f
    p.c.Wait()
    

    But Wait is never going to return, since at this point there's noone that will signal/broadcast it - the signal has already happened.

    So you need another condition that you can test yourself so you don't need to call Wait() when you already know the condition has happened, e.g.

    type processor struct {
        n       int
        c       *sync.Cond
        started chan int
        done    bool //added
    }
    
    ...
    
    func (p *processor) start() {
    
        for i := 0; i < p.n; i++ {
            go p.process(i)
        }
    
        for i := 0; i < p.n; i++ {
            <-p.started
        }
        p.c.L.Lock()
        p.done = true //added
        p.c.Broadcast()
        p.c.L.Unlock()
    
    }
    
    func (p *processor) process(f int) {
    
        fmt.Printf("fork : %d
    ", f)
    
        p.c.L.Lock()
        p.started <- f
        for !p.done { //added
            p.c.Wait()
        }
        p.c.L.Unlock()
        fmt.Printf("process: %d - out of wait
    ", f)
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 微信会员卡接入微信支付商户号收款
  • ¥15 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?