英文:
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
Donecall 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'sWaitmethod to loop:for some_boolean { c.Wait() }The code that calls
c.Signalorc.Broadcastwould set the boolean appropriately (here, tofalse) before callingSignalorBroadcast, to indicate that the waiter should now stop waiting. 
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论