我不知道如何正确使用sync.Cond
。据我所知,在锁定Locker和调用条件的Wait方法之间存在争用条件。这个例子在主goroutine中的两行之间添加了一个人工延迟来模拟竞争条件:
package main
import (
"sync"
"time"
)
func main() {
m := sync.Mutex{}
c := sync.NewCond(&m)
go func() {
time.Sleep(1 * time.Second)
c.Broadcast()
}()
m.Lock()
time.Sleep(2 * time.Second)
c.Wait()
}
[Run on the Go Playground]
这引起了恐慌:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Syncsemacquire(0x10330208, 0x1)
/usr/local/go/src/runtime/sema.go:241 +0x2e0
sync.(*Cond).Wait(0x10330200, 0x0)
/usr/local/go/src/sync/cond.go:63 +0xe0
main.main()
/tmp/sandbox301865429/main.go:17 +0x1a0
我做错了什么?如何避免这种明显的竞态条件?有没有更好的同步结构我应该使用?
- 编辑:**我意识到我应该更好地解释我在这里试图解决的问题。我有一个长时间运行的goroutine,它下载一个大文件,还有许多其他的goroutine,当HTTP头可用时,它们需要访问HTTP头。这个问题比听起来难。
我不能使用channels,因为只有一个goroutine会收到这个值。而其他一些goroutine会在头文件已经可用很久之后才试图检索它们。
下载器goroutine可以简单地将HTTP头存储在一个变量中,并使用互斥锁来保护对它们的访问。然而,这并没有为其他goroutine提供一种“等待”它们变得可用的方法。
我曾认为sync.Mutex
和sync.Cond
一起可以实现这个目标,但似乎这是不可能的。
9条答案
按热度按时间ryhaxcpt1#
OP回答了自己的问题,但没有直接回答原问题,我准备贴出如何正确使用
sync.Cond
。如果每次写和读都有一个goroutine,那么实际上并不需要
sync.Cond
--一个sync.Mutex
就足以在它们之间进行通信。sync.Cond
在多个读取器等待共享资源可用的情况下可能很有用。Playground
话虽如此,如果情况允许,使用通道仍然是推荐的数据传递方式。
注意:这里的
sync.WaitGroup
只用于等待goroutine完成它们的执行。u91tlkcl2#
您需要确保在调用c.Wait之后调用c.Broadcast。你的程序的正确版本应该是:
https://play.golang.org/p/O1r8v8yW6h
uoifb46i3#
http://play.golang.org/p/fBBwoL7_pm
pobjuy324#
看起来你c.等待广播,这在你的时间间隔中是永远不会发生的。与
你的片段似乎工作http://play.golang.org/p/OE8aP4i6gY .或者我错过了你试图实现的东西?
mtb9vblg5#
我终于发现了一种方法来做到这一点,它根本不涉及
sync.Cond
-只是互斥。这是怎么回事?
互斥锁在任务开始时被锁定,确保任何调用
WaitFor()
的操作都会被阻塞。一旦头可用并且goroutine解锁了互斥锁,每个对WaitFor()
的调用将一次执行一个。所有未来的调用(即使在goroutine结束之后)都不会有锁定互斥锁的问题,因为它总是处于解锁状态。n3ipq98p6#
是的,你可以使用一个通道将Header传递给多个Go例程。
ars1skjm7#
这可以很容易地用通道完成,代码也很干净。下面是一个例子。希望这有帮助!
p4tfgftt8#
在优秀的书“Concurrency in Go”中,他们提供了以下简单的解决方案,同时利用了关闭的通道将释放所有等待的客户端的事实。
https://play.golang.org/p/cE3SiKWNRIt
aij0ehis9#
代码中的问题是,信号只发出一次,而接收go例程还没有准备好,所以信号被错过了。你应该做循环广播。