英文:
How to be 100% sure that goroutines are waited on condition?
问题
我正在阅读Katherine Cox-Buday的《Go并发编程》一书,并且书中有一个示例,展示了如何使用Condition.Broadcast来唤醒等待的goroutine。以下是书中的示例代码:
package main
import (
"fmt"
"sync"
)
type Button struct {
Clicked *sync.Cond
}
func main() {
button := &Button{Clicked: &sync.Cond{L: &sync.Mutex{}}}
subscribe := func(c *sync.Cond, fn func()) {
var goroutineRunning sync.WaitGroup
goroutineRunning.Add(1)
go func() {
goroutineRunning.Done() //1
c.L.Lock()
// 2
defer c.L.Unlock()
c.Wait()
fn()
}()
goroutineRunning.Wait()
}
var clickedRegistred sync.WaitGroup
clickedRegistred.Add(2)
subscribe(button.Clicked, func() {
fmt.Println("Func #1")
clickedRegistred.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Func #2")
clickedRegistred.Done()
})
button.Clicked.Broadcast()
clickedRegistred.Wait()
}
这段代码是否百分之百地保证了并发安全?
是否可能出现这样的情况:主goroutine继续执行并在func goroutine被阻塞并等待条件信号/广播之前进行广播?
我的假设是:在//2的位置将//1的行移动,以避免这种情况。这样做是否有问题?或者说我描述的情况没有意义?
英文:
reading the book Concurrency in Go
by Katherine Cox-Buday and there is example how to use Condition.Broadcast to wake up waiting goroutines. here is the example from the book
<!-- language: lang-go -->
package main
import (
"fmt"
"sync"
)
type Button struct {
Clicked *sync.Cond
}
func main() {
button := &Button{Clicked: &sync.Cond{L: &sync.Mutex{}}}
subscribe := func(c *sync.Cond, fn func()) {
var goroutineRunning sync.WaitGroup
goroutineRunning.Add(1)
go func() {
goroutineRunning.Done() //1
c.L.Lock()
// 2
defer c.L.Unlock()
c.Wait()
fn()
}()
goroutineRunning.Wait()
}
var clickedRegistred sync.WaitGroup
clickedRegistred.Add(2)
subscribe(button.Clicked, func() {
fmt.Println("Func #1")
clickedRegistred.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Func #2")
clickedRegistred.Done()
})
button.Clicked.Broadcast()
clickedRegistred.Wait()
}
is it code 100% concurrency safe?
is it possible situation when main goroutine continue the work and does broadcast before func goroutines parked and waited the signal/broadcast from condition?
I mean the situation when func goroutines done their work at line //1 but did not acquire the condition lock yet and main goroutine do broadcast this time, is it possible?
my assumption the follow: should the line //1 be moved at place //2 to avoid this situation?
or there is no problem and the situation I discribed does not make sence?
答案1
得分: 1
你的问题有两个部分:
-
这段代码是否存在潜在的竞态条件漏洞?是的:可以参考 https://stackoverflow.com/q/58368947/1256452
-
将
Done
调用移动是否是一个修复方法?在这个特定的代码中,是的,但我认为这可能不是一个特别好的修复方法。正如Andy Schweig在评论中指出,使用条件的Wait
方法的代码更典型的做法是循环等待:for some_boolean { c.Wait() }
调用
c.Signal
或c.Broadcast
的代码在调用Signal
或Broadcast
之前会适当地设置布尔值(在这里设置为false
),以指示等待者现在应该停止等待。
英文:
Your question has two parts, I think:
-
Does this code have a potential race bug? Yes: see, e.g., https://stackoverflow.com/q/58368947/1256452
-
Is moving the
Done
call a fix? In this particular code, yes, but I would say, perhaps not a particularly good fix. As Andy Schweig notes in a comment on his own answer, it's more typical for code that uses the condition'sWait
method to loop:for some_boolean { c.Wait() }
The code that calls
c.Signal
orc.Broadcast
would set the boolean appropriately (here, tofalse
) before callingSignal
orBroadcast
, to indicate that the waiter should now stop waiting.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论