如何将WaitGroup传递给一个顺序函数调用?

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

How to pass WaitGroup to a sequential function call?

问题

我有一个函数,可以在单独的goroutine中按顺序或并发调用。

我想确保在主goroutine完成之前完全执行函数,所以我将*sync.WaitGroup参数传递给函数。现在,在某些地方,函数需要按顺序调用。

我可以像这样将nil waitGroup传递给函数:

  1. func my_func(wg *sync.WaitGroup){
  2. if wg != nil{
  3. defer wg.Done()
  4. }
  5. // 执行任务
  6. }
  7. func main(){
  8. my_func(nil) // 顺序调用
  9. wg := sync.WaitGroup{}
  10. wg.Add(1)
  11. go my_func(&wg) // 并发调用
  12. wg.Wait()
  13. }

有没有更好的方法来实现这个?

英文:

I have a function which can be called sequentially or concurrently in separate goroutine.

I want to ensure that function is executed completely before main goroutine finishes, so I am passing *sync.WaitGroup argument to the function. Now, at some places the function is to be called sequentially.

I can pass nil waitGroup to the function like this:

  1. func my_func(wg *sync.WaitGroup){
  2. if wg != nil{
  3. defer wg.Done()
  4. }
  5. // do the task
  6. }
  7. func main(){
  8. my_func(nil) // sequential call
  9. wg := sync.WaitGroup{}
  10. wg.Add(1)
  11. go my_func(&wg) // concurrent call
  12. wg.Wait()
  13. }

Is there any better way to achieve this ?

答案1

得分: 7

你的my_func()不应该知道/关心它是如何执行的(无论是在新的goroutine中还是其他方式)。因此,你不应该传递wg参数。不要强制要求并发或非并发使用你的API,让你的包的用户决定如何调用它。

如果有人希望并发地在新的goroutine中运行它,可以在my_func()之外处理wg,像这样:

  1. wg.Add(1)
  2. go func() {
  3. defer wg.Done()
  4. my_func()
  5. }()

这样还可以在函数调用之前/之后执行其他代码,这些代码将在wg.Done()调用之前执行:

  1. wg.Add(1)
  2. go func() {
  3. defer wg.Done()
  4. // 其他代码
  5. my_func()
  6. // 其他代码
  7. }()

另外请注意,如果你在多个地方都有这样的代码,你可以创建一个辅助函数来处理goroutine的启动和waitgroup的处理:

  1. func launchMyFunc(wg *sync.WaitGroup) {
  2. go func() {
  3. defer wg.Done()
  4. my_func()
  5. }()
  6. }

你还可以创建一个接受任意无参数无返回值函数的辅助函数:

  1. func launchFunc(wg *sync.WaitGroup, f func()) {
  2. go func() {
  3. defer wg.Done()
  4. f()
  5. }()
  6. }

使用上述辅助函数,你可以这样做:

  1. wg.Add(1)
  2. launchMyFunc(wg)
  3. // 或者
  4. wg.Add(1)
  5. launchFunc(wg, my_func)
英文:

Your my_func() should not know / should not care how it is executed (whether in a new goroutine or not). So just for this you should not pass wg. Do not enforce concurrent or non-concurrent use of your API, let users of your package decide how they wish to call it.

If someone wishes to run it concurrently, in a new goroutine, wg can be handled outside of my_func() like this:

  1. wg.Add(1)
  2. go func() {
  3. defer wg.Done()
  4. my_func()
  5. }()

This also gives possibility to put further code before / after the function call that will be executed before the wg.Done() call:

  1. wg.Add(1)
  2. go func() {
  3. defer wg.Done()
  4. // other code before
  5. my_func()
  6. // other code after
  7. }()

Also note that if you have this in many places, you can create a helper function that takes care of the goroutine launching and waitgroup handling:

  1. func launchMyFunc(wg *sync.WaitGroup) {
  2. go func() {
  3. defer wg.Done()
  4. my_func()
  5. }()
  6. }

You can also create a helper that accepts an arbitrary no-arg no-return function:

  1. func launchFunc(wg *sync.WaitGroup, f func()) {
  2. go func() {
  3. defer wg.Done()
  4. f()
  5. }()
  6. }

Using the above helpers this is how you could do the same:

  1. wg.Add(1)
  2. launchMyFunc(wg)
  3. // or
  4. wg.Add(1)
  5. launchFunc(wg, my_func)

huangapple
  • 本文由 发表于 2022年11月18日 19:12:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/74488807.html
匿名

发表评论

匿名网友

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

确定