I read a great article summarizing the interrelationship between go routines, channel communications, unix signals and proper termination by Adam Presley. In his article he describes how to coordinate starting a go routine, handling an interrupt, and then communicating through a channel to the go routine that the program wants to terminate.
So what do you do if you have more than one go routine? You need to communicate to all and wait for all when you quit. Here’s a contrived example that demonstrates one way to do it.
One point to note. In this example we don’t distinguish between which routines we wish to quit in any particular order. In fact, as implemented here, there is no deterministic way of knowing the order (How might you implement the code so that you would be able to deterministically know the order of go routine termination?)
Here’s an example that demonstrates how you might handle an arbitrary number of go routines:
Let’s say we start off with a constant number of routines we wish to create:
1 2 3 |
|
We will start a go routine maxGoRoutines times and then we will ensure that we wait for the same number of routines to complete by using a waitGroup
1 2 |
|
Now let’s define a simple go routine. We’ll pass a channel to let us know when to quit, a waitGroup to indicate that we’ve quit once we’ve left the routine, and an identifier to distinguish between go routines to make our demo look cool!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Once we’ve launed the routines, we’ll wait for the program to terminate. We’ve established a signal handler to let us know when SIGTERM or SIGQUIT by the following lines:
1 2 |
|
Next, we’ll wait to receive a signal that we’ve quit by blocking on the quitChannel. Once we receive a message indicating that we’ve quit, we’ll send a boolean true to our go routine shutdownChannel. Notice that we have to send as many messages to this channel as we have go routines. Otherwise, we’ll leave go routines hanging around and that will block us from terminating.
And finally, we wait for the waitGroup to complete. After each go routine calls its defered waitGroup.Done() function, we will unblock on the waitGroup.Wait() and can successfully exit!
1 2 |
|
Here’s the whole thing from soup to nuts!
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
|