Starting and stopping things with a signal channel

Mat Ryer
2 min readDec 2, 2015

--

Channels in Go are a great way to communicate data between goroutines, but you can also use them just for signalling.

When doing this, it’s good practice to use an empty struct as the type of the channel, since they’re pretty useless for anything other than signalling — it expresses intent in your code.

It also has the interesting property of not taking up much space in memory — since an empty struct has no fields inside it. See for yourself.

This is a signal channel:

var signal chan struct{}

And you make channels using Go’s build-in make function:

signal := make(chan struct{})

Code can block waiting for something to be sent on the channel:

<-signal

In this case we don’t care about the value, which is why we don’t assign it to anything.

Closing the channel, will unblock it, allowing execution to continue.

Similarly, inside a select block, a closed channel will trigger the case allowing us to run different code when we receive such a signal from somewhere outside of the goroutine.

Wait for something to finish

By blocking on a signal channel, you can wait for a task in another goroutine to finish:

done := make(chan struct{})go func() {
doLongRunningThing()
close(done)
}()
// do some other bits// wait for that long running thing to finish
<-done
// do more things

Start lots of things at the same time

If we queue up lots of goroutines, we can have them all start at the same time by closing the signal channel.

start := make(chan struct{})for i := 0; i < 10000; i++ {
go func() {
<-start // wait for the start channel to be closed
doWork(i) // do something
}()
}
// at this point, all goroutines are ready to go - we just need to
// tell them to start by closing the start channel
close(start)

Stopping things

A similar technique can be used to stop goroutines. Check out this goroutine that is happily sending emails whenever it receives one down the `email` channel:

loop:
for {
select {
case m := <-email:
sendEmail(m)
case <-stop: // triggered when the stop channel is closed
break loop // exit
}
}

Now, if the `stop` channel is closed the for loop will exit and no more emails will be sent.

--

--

Mat Ryer

Founder at MachineBox.io — Gopher, developer, speaker, author — BitBar app https://getbitbar.com — Author of Go Programming Blueprints