Wait groups

The concept of wait groups is very important for building production level software in Go; it allows you to wait for multiple goroutines to finish before you proceed with the rest of your code.

To fully grasp the benefit of wait groups, let's go back to an earlier code sample:

package main

import (
"fmt"
"time"
)

func runLoopSend(n int, ch chan int) {
for i := 0; i < n; i++ {
ch <- i
}
close(ch)
}

func runLoopReceive(ch chan int) {
for {
i, ok := <-ch
if !ok {
break
}
fmt.Println("Received value:", i)
}
}

func main() {
myChannel := make(chan int)
go runLoopSend(10, myChannel)
go runLoopReceive(myChannel)
time.Sleep(2 * time.Second)
}

In the preceding code, we had to make the main goroutine sleep for two seconds in order to wait for the other two goroutines to finish. However, what if the other goroutines took more than two seconds? It was never guaranteed that this simple sleep would produce the result that we are seeking. Instead, we could have done the following:

package main

import (
"fmt"
"sync"
)

// Create a global waitgroup:
var wg = &sync.WaitGroup{}

func main() {
myChannel := make(chan int)
//Increment the wait group internal counter by 2
wg.Add(2)
go runLoopSend(10, myChannel)
go runLoopReceive(myChannel)
//Wait till the wait group counter is 0
wg.Wait()
}

func runLoopSend(n int, ch chan int) {
//Ensure that the wait group counter decrements by one after //our function exits
defer wg.Done()
for i := 0; i < n; i++ {
ch <- i
}
close(ch)
}

func runLoopReceive(ch chan int) {
//Ensure that the wait group counter decrements after our //function exits
defer wg.Done()
for {
i, ok := <-ch
if !ok {
break
}
fmt.Println("Received value:", i)
}
}

A WaitGroup struct type in Go is a type that keeps an internal counter; as long as the internal counter is not 0, the wait group will block your goroutine. In the preceding code, we created a global pointer variable to WaitGroup; we called it wg. This variable will be visible to all of our functions in this simple program. Before we triggered the two goroutines, we incremented the wait group internal counter by 2 using the wg.Add(2) method. After that, we proceeded to create our two goroutines. For each of the goroutines we added the following code:

defer wg.Done()

This uses a combination of defer and the wg.Done() method in order to ensure that whenever the goroutine function finishes execution, wg.Done() gets called. The wg.Done() method will decrement the internal wait group counter by one.

Finally, at the end of our main goroutine, we call wg.Wait(), which will block the current goroutine until the internal counter of the wait group is zero. This will, in turn, force the main goroutine to wait until all the goroutines in our program finish executing.

The final output to the preceding code is as follows:

Received value: 0
Received value: 1
Received value: 2
Received value: 3
Received value: 4
Received value: 5
Received value: 6
Received value: 7
Received value: 8
Received value: 9
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.37.10