This is the second article of a series analyzing Go concurrency, ie, parallel execution of different threads using Go. The series is composed of these articles:
- The Go concurrency foundation: goroutines.
- This one, in which synchronization between subprocesses, specifically by using Go waitgroups, is analyzed.
- Data exchange between subprocesses: Go channels
Why synchronization between subprocesses
The first article of the series exposed an example that clearly demonstrated why the subprocess synchronization is needed specifically in go:
package main
import (
"fmt"
"time"
)
// mySubprocess sleeps for a second
func mySubprocess() {
fmt.Println("Entering to mySubprocess")
time.Sleep(1 * time.Second)
fmt.Println("Exiting from mySubprocess")
}
// main program process
func main() {
fmt.Println("Calling mySubprocess from main()...")
go mySubprocess()
fmt.Println("mySubprocess finished!")
}
(Try the above code in Go Playground)
The output of the above code shows how Go forgets about their running (and scheduled) subprocesses once the main process is finished:
Calling mySubprocess from main()...
mySubprocess finished!
As seen, the main process has finished and Go has not executed mySubprocess(). Even if Go had started running it, it would have killed in in the event of the main process finishing.
Basic process syncing: Wait Groups
The Go sync package contains most of the tools used in the everyday process syncing, being the WaitGroup one of the most basic and useful tools.
As its name promises, the WaitGroup
allows us pausing a process until a group of processes finish their job. Better showing it into action by fixing the problem we found in the above code:
package main
import (
"fmt"
"sync"
"time"
)
// mySubprocess sleeps for a second
func mySubprocess(wg *sync.WaitGroup) {
fmt.Println("Entering to mySubprocess")
time.Sleep(1 * time.Second)
fmt.Println("Exiting from mySubprocess")
// Decrement the items in the wait group by one
wg.Done()
}
// main program process
func main() {
var wg sync.WaitGroup
// Increment the items in the wait group by one
wg.Add(1)
fmt.Println("Calling mySubprocess from main()...")
go mySubprocess(&wg)
fmt.Println("mySubprocess called!")
// Wait until the number of items
// in the wait group was zero
wg.Wait()
fmt.Println("main program finished")
}
(Try it in Go Playground)
Now the process output looks much more predictible:
Calling mySubprocess from main()...
mySubprocess called!
Entering to mySubprocess
Exiting from mySubprocess
main program finished
An improved real example
In the first article of this series we introduced an example for concurrently calculating some Fibonacci sequences. Then we used a very rudimentary way of waiting for the subprocesses to finish (calculating a more computing-costly Fibbonacci sequence), but by one program execution to other chances were of not waiting for all the sequences calculation completion.
Now, using wait groups the problem can be easily fixed, as shown in the next code example:
package main
import (
"fmt"
"math"
"sync"
)
// getFibonacci calculates the Fibonacci sequence
// for a given number
func getFibonacci(n float64) float64 {
if n <= 1 {
return n
}
n2, n1: = 0.0, 1.0
for i: = 2.0;
i <= n;
i++{
n2, n1 = n1, n1 + n2
}
return n1
}
// mySubprocess calculates the Fibbonacci sequence
// for a given number and prints it
func printFibonacci(n float64, wg *sync.WaitGroup) {
fmt.Printf(
"Calculating the Fibbonacci sequence for number %.0f\n", n)
v: = getFibonacci(n)
fmt.Printf(
"The Fibonacci sequence for %.0f is %.0f\n", n, v)
wg.Done()
}
// main process
func main() {
var wg sync.WaitGroup
for i:=7; i >= 0; i-- {
wg.Add(1)
go printFibonacci(math.Pow(2, float64(i)), &wg)
}
wg.Wait()
}
Now the program output looks like this:
Calculating the Fibbonacci sequence for number 1
Calculating the Fibbonacci sequence for number 128
Calculating the Fibbonacci sequence for number 32
The Fibonacci sequence for 32 is 2178309
Calculating the Fibbonacci sequence for number 16
The Fibonacci sequence for 16 is 987
Calculating the Fibbonacci sequence for number 2
The Fibonacci sequence for 2 is 1
The Fibonacci sequence for 128 is 251728825683549523871268864
Calculating the Fibbonacci sequence for number 64
The Fibonacci sequence for 64 is 10610209857723
The Fibonacci sequence for 1 is 1
Calculating the Fibbonacci sequence for number 4
The Fibonacci sequence for 4 is 3
Calculating the Fibbonacci sequence for number 8
The Fibonacci sequence for 8 is 21
Comentarios
Publicar un comentario