首页 > 解决方案 > sync.Cond 是如何工作的?

问题描述

我试图了解如何工作sync.Cond,但它对我不起作用。我的代码

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

var wg sync.WaitGroup
wg.Add(1)

go func() {
    defer wg.Done()
    c.L.Lock()
    defer c.L.Unlock()
    c.Wait()
    fmt.Println("I'm created")
}()

c.Signal()
wg.Wait()

我总是陷入僵局,但如果我在什么都没有出现defer之前删除,但wg.Done()在这种情况下我不会收到错误。我看过其他例子,但它们是不同的。我想使用我的示例了解基本思想 sync.Cond。我不知道这里哪里出错了,因为 goroutine 运行并且没关系,然后它等待一个信号,但是在主 goroutine 我这样做 c.Signal() ,因此我希望 goroutine 能够恢复,但它不会发生......

标签: go

解决方案


作为一般规则,1当你使用一个sync.Cond变量时,你需要做件事:

  • 互斥体,和
  • 互斥锁正在保护的数据结构。

在该数据结构中,您需要有一些变量来告诉您条件本身已满足或未满足。

这导致这种形式的代码:

type whatever struct {
    ready  bool
    done   bool
    input  string
    result string
    lock   sync.Mutex
    cond   *sync.Cond
}

func doStuff(p *whatever) {
    p.lock.Lock()
    defer p.lock.Unlock()
    for !p.ready && !p.done {
        p.cond.Wait()
    }
    fmt.Printf("working on thing with input=%q\n", p.input)
    p.result = "all done now"
    p.done = true
    p.cond.Signal()
}

func waitDone(p *whatever) {
    p.lock.Lock()
    defer p.lock.Unlock()
    for !p.done {
        p.cond.Wait()
    }
}

然后您可以从其他地方使用它,例如:

    var obj whatever
    obj.cond = sync.NewCond(&obj.lock)
    go doStuff(&obj)
    obj.lock.Lock()
    obj.input = "some input"
    obj.ready = true // object is now ready to be worked-on
    obj.lock.Unlock()
    obj.cond.Signal()
    waitDone(&obj)
    fmt.Printf("obj is done, result is %q\n", obj.result)

这里有一个完整的例子。

现在,正如所写的,没有理由在sync.Cond这里使用 a 。对于这种情况,仅使用互斥锁就足够了。sync.Cond多个并发例程需要等待某个事件时,我们确实使用了一个。例如,多个 goroutine 可能需要该对象:一个人应该完成工作,然后应该将该对象标记为就绪,并使用广播提醒所有等待该对象的人。在这种情况下,doStuff()将以p.cond.Broadcast()而不是结束p.cond.Signal()

在其他一些情况下,我们可能有一个对象在不同的​​ goroutine 之间进行乒乓操作。一个或多个 goroutine 正在或至少可能正在等待该对象。其中一个——让我们称之为这个特定的 goroutine Fred——获得了它的控制权并在对象上做一些有限的工作,然后声明这个goroutine 已经完成。Fred 将对象恢复到合适的状态(完成了一些但不是全部工作)并调用p.cond.Signal(),这会唤醒其他两个都在等待对象的 goroutine 中的一个。无论哪个被唤醒,都会获取该对象,对其进行更多工作,然后将其恢复到合适的状态并调用p.cond.Signal()以向其他任何人发出等待的信号。

最终,工作全部完成,完成工作的人设置p.done并打电话p.cond.Broadcast()提醒所有可能处理p该工作的人p现在已经完成。

条件变量比简单的互斥锁更灵活,但需要更多的思考和关注。如果一个简单的互斥锁就可以完成工作,通常最好只使用互斥锁。


1在某些情况下您可以违反此规则,但如果您准备好考虑这些情况,您一开始就不会遇到此问题。


推荐阅读