Go Interview Question: Use Three Goroutines to Print cat, dog, and fish 100 Times Each in Sequence

Interview Question

This is a classic Golang interview question that tests your understanding of channels. The requirements are as follows:

  1. Start three goroutines.
  2. The three goroutines must print cat dog fish cat dog fish... in sequence.
  3. Each goroutine must print 100 times, for a total of 300 outputs.

Implementation Approach

We will refer to the three goroutines as g0, g1, and g2. Since they must print alternately in sequence, when g0 is running, g1 and g2 need to block and wait. After g0 finishes, it must notify g1 to run. After g1 finishes, it must notify g2, and so on.

We know that receiving a message from an unbuffered channel, or from a buffered channel whose buffer is empty, causes the goroutine to block and wait. Therefore, we can pass two channels to each printing goroutine: one for receiving a signal to execute, and another for notifying the next goroutine to execute.

At the same time, we can use a sync.waitGroup to block the main goroutine until all child goroutines have finished.

Implementation Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
	"fmt"
	"sync"
)

// Interview questions: one goroutine per function, printing cat dog fish every 100 times
// 3 Goroutine, print order is cat dog fish cat dog fish... and so on.
var wg sync.WaitGroup

func main() {
	chCatOk := make(chan struct{}, 1)
	chDogOk := make(chan struct{}, 1)
	chFishOk := make(chan struct{}, 1)
	wg.Add(3) // There were three courses, so I added three.
	go printAnimal("cat", chCatOk, chDogOk)
	go printAnimal("dog", chDogOk, chFishOk)
	go printAnimal("fish", chFishOk, chCatOk)
	// Notify cat first.
	chCatOk <- struct{}{}
	wg.Wait()
	fmt.Println("执行结束")
}

func printAnimal(word string, ch1 <-chan struct{}, ch2 chan<- struct{}) {
	count := 0
	// Mark completed before exit
	defer wg.Done()
	for _ = range ch1 {
		fmt.Println(word)
		count++
		ch2 <- struct{}{} // Notify goroutine 2 that it may proceed.
		if count == 100 {
			return
		}
	}
}

Code Execution

The output is as follows:

image-20230911195718738

Further Considerations

In the code above, can the following three lines be changed to initialize unbuffered channels? If not, why?

1
2
3
chCatOk := make(chan struct{}, 1)
chDogOk := make(chan struct{}, 1)
chFishOk := make(chan struct{}, 1)

(The End)

Built with Hugo
Theme Stack designed by Jimmy