英文:
Understanding sync.cond in golang
问题
最近我开始学习Go语言,并遇到了sync.Cond函数。我对它的工作原理有一些困惑。
考虑下面的代码:
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
mu := sync.Mutex{}
c := sync.NewCond(&mu)
wg.Add(1)
go func() {
defer wg.Done()
c.L.Lock()
// for len(sharedRsc) == 0 {
c.Wait()
// }
c.L.Unlock()
fmt.Println(sharedRsc["rsc1"])
}()
// writes changes to sharedRsc
c.L.Lock()
sharedRsc["rsc1"] = "foo"
c.Signal()
c.L.Unlock()
wg.Wait()
}
当执行这段代码时,会导致死锁。我理解原因可能是在c.Signal之前调用了c.Wait。
但我不明白的是,当我们取消注释for循环时,它就能正常工作。那么这个for循环是如何解决这个竞态条件的呢?即使在这里,c.Signal在c.Wait之前仍有可能被调用,对吗?
英文:
I have started learning golang recently and I came across this sync.Cond function. I have little trouble understanding how this works.
consider the below code
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
mu := sync.Mutex{}
c := sync.NewCond(&mu)
wg.Add(1)
go func() {
defer wg.Done()
c.L.Lock()
// for len(sharedRsc) == 0 {
c.Wait()
// }
c.L.Unlock()
fmt.Println(sharedRsc["rsc1"])
}()
// writes changes to sharedRsc
c.L.Lock()
sharedRsc["rsc1"] = "foo"
c.Signal()
c.L.Unlock()
wg.Wait()
}
when this is executed it leads to a deadlock. I understand the reason might be c.Signal is called before c.Wait.
But what I don't understand is when we uncomment the for loop it works. So how does the for loop solves this race condition? Even here there is a possibility c.Signal can be called before c.Wait right?
答案1
得分: 1
你将需要使用for循环,因为如果在第二个等待协程之前发出信号,它将在Wait()处发生死锁,因为之后没有人会发出信号。
考虑以下执行流程:
- 修改
sharedRsc
- c.Signal()
- wg.Wait()
- c.Wait()
英文:
You will need the for loop because if the cond is signaled before the second coroutine waiting, it will deadlock at the Wait() because no one will signal after that.
Consider the following execution flow:
- Modify
sharedRsc
- c.Signal()
- wg.Wait()
- c.Wait()
答案2
得分: -1
这是正确使用sync.Cond
的方法。
如果每个写入和读取操作都有一个goroutine,实际上不需要使用sync.Cond
- 一个单独的sync.Mutex
就足够在它们之间进行通信了。sync.Cond
在多个读取器等待共享资源可用的情况下非常有用。
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
wg.Add(2)
m := sync.Mutex{}
c := sync.NewCond(&m)
go func() {
// 这个goroutine等待sharedRsc的变化
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc1"])
c.L.Unlock()
wg.Done()
}()
go func() {
// 这个goroutine等待sharedRsc的变化
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc2"])
c.L.Unlock()
wg.Done()
}()
// 这个goroutine将变化写入sharedRsc
c.L.Lock()
sharedRsc["rsc1"] = "foo"
sharedRsc["rsc2"] = "bar"
c.Broadcast()
c.L.Unlock()
wg.Wait()
}
你可以在Go Playground中运行它。
如果情况允许,使用通道仍然是传递数据的推荐方式。
注意:这里的sync.WaitGroup
仅用于等待goroutine完成执行。
为了更好地理解,请参考官方文档:https://pkg.go.dev/sync
还可以参考类似的Stack Overflow问题:https://stackoverflow.com/questions/36857167/how-to-correctly-use-sync-cond
英文:
This is how to correctly use sync.Cond
.
You do not really need sync.Cond
if you have one goroutine for each write and read - a single sync.Mutex
would suffice to communicate between them. sync.Cond
could useful in situations where multiple readers wait for the shared resources to be available.
var sharedRsc = make(map[string]interface{})
func main() {
var wg sync.WaitGroup
wg.Add(2)
m := sync.Mutex{}
c := sync.NewCond(&m)
go func() {
// this go routine wait for changes to the sharedRsc
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc1"])
c.L.Unlock()
wg.Done()
}()
go func() {
// this go routine wait for changes to the sharedRsc
c.L.Lock()
for len(sharedRsc) == 0 {
c.Wait()
}
fmt.Println(sharedRsc["rsc2"])
c.L.Unlock()
wg.Done()
}()
// this one writes changes to sharedRsc
c.L.Lock()
sharedRsc["rsc1"] = "foo"
sharedRsc["rsc2"] = "bar"
c.Broadcast()
c.L.Unlock()
wg.Wait()
}
You can it in Go Playground.
Using channels is still the recommended way to pass data around if the situation permitting.
Note: sync.WaitGroup
here is only used to wait for the goroutines to complete their executions.
For better understanding refer the official docs : https://pkg.go.dev/sync
And also see the similar SO : https://stackoverflow.com/questions/36857167/how-to-correctly-use-sync-cond
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论