Synchronizing Goroutines - Concurrency in Golang




Welcome everyone to part 19 of the Go Language programming tutorial series, where we've been covering concurrency in Go, but have quickly come across a problem where our goroutines will not necessarily finish by the time our program does. We need a way to wait for them as necessary.

Besides waiting for your goroutines to finish like in our case, where they aren't dependent on eachother, there are also going to be times when you want to run a group of goroutines, wait for them to all finish, then run another group. Again, we need some way, other than an arbitrary sleep, to make this happen. To do it, we'll use the sync package from the standard library.

Our code up to this point:

package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
}

func main() {
	go say("Hey")
	go say("there")
	time.Sleep(1000*time.Millisecond)
}

This code works fine, but the time.Sleep(1000*time.Millisecond) is unfortunate. Instead of sleeping, we're going to use sync.WaitGroup. To begin, let's import sync, and define the WaitGroup variable:

package main

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

var wg sync.WaitGroup

Once we have a "wait group," we can add goroutines to this group:

func main() {
	wg.Add(1)
	go say("Hey")
	wg.Add(1)
	go say("there")
}

We can add whatever we want, and then specify that we want to wait for this group of goroutines with wg.Wait()

func main() {
	wg.Add(1)
	go say("Hey")
	wg.Add(1)
	go say("there")
	wg.Wait()
}

Now, the "wait group" only knows it needs to finish some goroutines, but it doesn't really know when or which ones. For this reason, we need our goroutine to tell the wait group it is done, with wg.Done

func say(s string) {
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
	wg.Done()
}

Full code up to this point:

package main

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

var wg sync.WaitGroup

func say(s string) {
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
	wg.Done()
}

func main() {
	wg.Add(1)
	go say("Hey")
	wg.Add(1)
	go say("there")
	wg.Wait()
}

Awesome! This runs, and everything is right with the world!

...but what if, for whatever reason, the say function breaks, and we never make it to wg.Done()? That...could be problematic. In order to handle for this, we need to begin discussing defer, panic, and recover, which we'll begin in the next tutorial.

The next tutorial:





  • Introduction to the Go Programming Language
  • Go Language Syntax
  • Go Language Types
  • Pointers in Go Programming
  • Simple Web App in Go Programming
  • Structs in the Go Programming Language
  • Methods in Go Programming
  • Pointer Receivers in Go Programming
  • More Web Dev in Go Language
  • Acessing the Internet in Go
  • Parsing XML with Go Programming
  • Looping in Go Programming
  • Continuing our Go Web application
  • Mapping in Golang
  • Mapping Golang sitemap data
  • Golang Web App HTML Templating
  • Applying templating to our Golang web app
  • Goroutines - Concurrency in Goprogramming
  • Synchronizing Goroutines - Concurrency in Golang
  • Defer - Golang
  • Panic and Recover in Go Programming
  • Go Channels - Concurrency in Go
  • Go Channels buffering, iteration, and synchronization
  • Adding Concurrency to speed up our Golang Web Application