Chapter 13 - Goroutines and Channels

Exercise 1: Goroutines and Channels

Here’s our program that runs the a and b functions in goroutines again. Previously, we used time.Sleep(time.Second) to keep the main goroutine from exiting until the other goroutines finished. But these functions don’t take anywhere near a second to finish, meaning it takes longer than necessary for the program to complete.

Revise the a and b functions to accept a channel as a parameter. Each function should send a value to the channel when its work is complete. We just want to use the channel for synchronization, so the values sent don’t really matter; we suggest bool values.

In main, you’ll need to create the channel you’re going to use. Call a in a new goroutine, passing the channel as an argument. Do the same for b. (You can pass a single channel to both functions.) Finally, read from the channel twice. Each read will cause the main goroutine to block, allowing the other two goroutines to complete before the program exits.

Solution

package main

import "fmt"

// Accept a channel as a parameter.
func a(done chan bool) {
	for i := 0; i < 50; i++ {
		fmt.Print("a")
	}
	// Write a value to the channel when done printing.
	done <- true
}

// Accept a channel as a parameter.
func b(done chan bool) {
	for i := 0; i < 50; i++ {
		fmt.Print("b")
	}
	// Write a value to the channel when done printing.
	done <- true
}

func main() {
	// Create the channel.
	done := make(chan bool)
	// Run each function as a goroutine, passing the
	// channel to each.
	go a(done)
	go b(done)
	// Read from the channel, which will block until the
	// first goroutine is done. (We don't know which
	// goroutine will finish first, and for this example,
	// it doesn't matter.) We'll just discard the value
	// we read.
	<-done
	// Block until the second goroutine is done.
	<-done
	fmt.Println("end main()")
}

Example Output:

bbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaend main()