goconcurrencywaitgroup

How to pass WaitGroup to a sequential function call?


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:

func my_func(wg *sync.WaitGroup){
   if wg != nil{
   defer wg.Done()
   }
   // do the task
}

func main(){
my_func(nil) // sequential call

wg := sync.WaitGroup{}
wg.Add(1)
go my_func(&wg)  // concurrent call
wg.Wait()
}

Is there any better way to achieve this ?


Solution

  • 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:

    wg.Add(1)
    go func() {
        defer wg.Done()
        my_func()
    }()
    

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

    wg.Add(1)
    go func() {
        defer wg.Done()
    
        // other code before
        my_func()
        // other code after
    }()
    

    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:

    func launchMyFunc(wg *sync.WaitGroup) {
        go func() {
            defer wg.Done()
    
            my_func()
        }()
    }
    

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

    func launchFunc(wg *sync.WaitGroup, f func()) {
        go func() {
            defer wg.Done()
    
            f()
        }()
    }
    

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

    wg.Add(1)
    launchMyFunc(wg)
    
    // or
    wg.Add(1)
    launchFunc(wg, my_func)