calling wait group done right after go routine starts?

huangapple go评论81阅读模式
英文:

calling wait group done right after go routine starts?

问题

这段代码是关于使用广播(broadcast)来唤醒三个 goroutine 的示例。在这段代码中,作者定义了一个名为 subscribe 的函数,用于注册处理条件信号的函数。每个处理函数都在自己的 goroutine 中运行,并且 subscribe 函数在确认该 goroutine 正在运行之前不会退出。

在这段代码中,我们将 goroutineRunning.Done() 放在 goroutine 的开头,而不是使用 defer 延迟执行。这是因为如果将其放在其他位置,就会导致死锁错误。这样做的原因是确保在等待条件信号和执行 fn() 调用之前,goroutineRunning.Done() 已经执行,以避免死锁。

更新部分的代码中,我们可以看到可以通过传递一个 sync.WaitGroup 指针来消除 subscribe 函数中的 WaitGroup。这样做的好处是可以更灵活地控制 WaitGroup 的使用。

英文:

https://go.dev/play/p/YVYRWSgcp4u

I'm seeing this code in "Concurrency in Go Tools and Techniques for Developers", where it's mentioned about the usage for broadcast, the context is to use broadcast to wake three goroutings.

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {

	type Button struct {
		Clicked *sync.Cond
	}
	button := Button{Clicked: sync.NewCond(&sync.Mutex{})}

	subscribe := func(c *sync.Cond, fn func()) {
		var goroutineRunning sync.WaitGroup
		goroutineRunning.Add(1)
		go func() {
			goroutineRunning.Done() // <---- why here?
			//fmt.Println("wg already done")
			c.L.Lock()
			defer c.L.Unlock()
			c.Wait()
			fn()
			//goroutineRunning.Done(), if put here will result in deadlock, why?
			
		}()
		goroutineRunning.Wait()
	}

	var clickRegistered sync.WaitGroup
	clickRegistered.Add(3)

	subscribe(button.Clicked, func() {
		fmt.Println("Maximizing window.")
		clickRegistered.Done()
	})
	subscribe(button.Clicked, func() {
		fmt.Println("Displaying annoying dialog box!")
		clickRegistered.Done()
	})
	subscribe(button.Clicked, func() {
		fmt.Println("Mouse clicked.")
		clickRegistered.Done()
	})

	time.Sleep(time.Second * 3)
	button.Clicked.Broadcast()
	clickRegistered.Wait()

}

I'm trying to understand the subscribe part

subscribe := func(c *sync.Cond, fn func()) {
		var goroutineRunning sync.WaitGroup
		goroutineRunning.Add(1)
		go func() {
			goroutineRunning.Done()
			//fmt.Println("wg already done")
			c.L.Lock()
			defer c.L.Unlock()
			c.Wait()
			fn()
			//goroutineRunning.Done()
			//fmt.Println("fn executed")
		}()
		goroutineRunning.Wait()
	}

The author says:
> Here we define a convenience function that will allow us to register functions to handle signals from a condition. Each handler is run on its own goroutine, and subscribe will not exit until that goroutine is confirmed to be running.

My understanding is that we should defer goroutingRunning.Done() inside gorouting so that the subsequent code (including waiting for Cond and fn() call, will have opportunities
to run), but in this case it seems the goroutingRunning.Done() has to be in the beginning of gorouting, otherwise it would result in deadlock error, but why?

------UPDATE------

We could actually get rid of the waitgroup in subscribe function this way:

   subscribe := func(c *sync.Cond, fn func(), wg *sync.WaitGroup) {
		c.L.Lock()
		defer c.L.Unlock()
		c.Wait()
		fn()
		wg.Done()
	}

	var ClickRegistered sync.WaitGroup
	ClickRegistered.Add(3)
	go subscribe(button.Clicked, func() {
		fmt.Println("do 1")
	}, &ClickRegistered)
	go subscribe(button.Clicked, func() {
		fmt.Println("do 2")
	}, &ClickRegistered)
	go subscribe(button.Clicked, func() {
		fmt.Println("do 3")
	}, &ClickRegistered)

	time.Sleep(time.Millisecond * 50)
	fmt.Println("some process in main go routine finished")
	button.Clicked.Broadcast()
	ClickRegistered.Wait()

答案1

得分: 1

这是一种机制,确保在subscribe返回时,goroutine已经开始运行。当goroutine启动时,它调用Done来向等待的调用者发出信号,表示goroutine正在运行。如果没有这个机制,可能在subscribe返回时,goroutine还没有被调度。

延迟的Done不起作用,因为它只会在goroutine返回时运行,而在条件变量被信号通知之前,goroutine不会返回。

该方案不能保证新的goroutine锁定互斥锁。是否真的需要这种方案还有争议。

英文:

This is a mechanism that ensures that when subscribe returns, the goroutine has started running. When goroutine starts, it calls Done to signal to the waiting caller that the goroutine is running. Without this mechanism, it is possible that when subscribe returns the goroutine has not been scheduled yet.

A deferred Done will not work, because that will only run once the goroutine returns, and that will not happen until the condition variable is signaled.

The scheme does not guarantee that the new goroutine locks the mutex. It is debatable whether this schema is really necessary.

huangapple
  • 本文由 发表于 2022年2月25日 00:22:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/71255142.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定