TTuck

We can use channels to synchronize execution across goroutines. Here's an example of using a blocking receive to wait for a goroutine to finish. When waiting for multiple goroutines to finish, you may prefer to use a WaitGroup.

package main

import (
	"fmt"
	"time"
)

This is the function we'll run in a goroutine. The done channel will be used to notify another goroutine that this function's work is done.

func worker(done chan bool) {
	fmt.Print("working...")
	time.Sleep(100 * time.Millisecond)
	fmt.Println("done")

Send a value to notify that we're done.

	done <- true
}

func main() {

Start a worker goroutine, giving it the channel to notify on.

	done := make(chan bool, 1)
	go worker(done)

Block until we receive a notification from the worker on the channel.

	<-done

If you removed the <-done line, the program would exit before the worker even started. A channel of struct{} is common when only the signal matters, and closing it signals every waiting receiver at once.

	ready := make(chan struct{})
	go func() {
		fmt.Println("setting up")
		close(ready)
	}()
	<-ready
	fmt.Println("ready")

The receive happens after the matching send, so writes done before the send are visible after the receive. Unlocking a mutex and a later lock of that same mutex give the same guarantee.

	observed := ""
	sent := make(chan struct{})
	go func() {
		observed = "from the sender"
		close(sent)
	}()
	<-sent
	fmt.Println(observed)
}
$ go run ./35_channel_synchronization
working...done
setting up
ready
from the sender