Table of Contents
Concurrency is one of the central features of Go. And, to build concurrent programs in Go, you need goroutines.
A goroutine is like a thread, but lighter. Much lighter. And, like any other built-in feature of Go, using it is dead simple:
Wait. That didn’t print anything.
This is because when the
main function of a Go program returns, it will abort all goroutines. Go will not wait until these goroutines finish running. But you can wait for goroutines to finish running.
In this blog post, we will take a look different ways of waiting for one or more goroutines to finish running.
Using a Channel
If you want to wait on one goroutine, you could go the primitive route: use a channel.
Create a channel, of any type. From within the goroutine, at the very end of the function, send a value to the channel. And where you want to wait for the goroutine to finish, read from the channel.
The read will block until there is something in the channel to read.
You could close the channel
donech instead of sending a value over it to signal multiple readers that the goroutine has finished running.
If you want to wait on multiple goroutines, then read from the channel as many times as there are goroutines.
In the example above, you are reading from the channel
n times. The loop, reading from the channel, can exit only when there are
n values to read from the channel.
The standard library has a neat abstraction for this:
sync.WaitGroup type provides three methods:
Add(delta int): Add counts the number of goroutines we are waiting for.
Done(): Done decrements the count by 1.
Wait(): Wait blocks until the counter is zero.
sync.WaitGroup, you will
Add the number of goroutines to wait for. From within each goroutine, call
Done right before the function returns.
Wait from where you want to wait on the goroutines.
Until One of Several Goroutines Fails (Using
If you have several goroutines and you want to stop as soon as any one of them experiences an error, then you could use Go’s almost standard package
This package provides the handy
errgroup.Group type that can run multiple goroutines and return the first non-nil error, if any.
Aborting the Rest
If your program doesn’t exit, the remaining goroutines will continue to run and consume resources. Depending on what these goroutines are doing you could use a cancellable
context.Context to signal the remaining goroutines to be aborted.
Until a Timeout
You can do wonderful things in Go with channels.
time.NewTimer function returns a
time.Timer with a channel. The channel is closed when the timer expires. By calling
time.NewTimer with a duration, the channel will close after the given duration has elapsed.
By combining multiple channel operations using a
select statement, you can wait on different conditions. For example, either wait on all goroutines to send values over a channel, or wait for a timer to expire.
In the code above, the second loop will exit when all goroutines have sent messages over
donech. Or, the timer
t has expired.
Similarly, you could wait until the user presses
Ctrl+C (i.e. sends an interrupt from the terminal) by using a signal channel.
In Go, channels are the primitive for synchronization.
If you want to wait on a
goroutine you have to use a channel; whether it is as a primitive or through a package.
comments powered by Disqus