Done channels
If you need multiple goroutines to wait for the same thing, use a done channel.
A done channel is defined like this.
1
var done = make(chan struct{})
Defining it as a chan struct{}
indicates to readers that no data will be sent on this channel and that it is only used for signaling.
Once the thing you want finished is completed, close the channel. This will immediately unblock any goroutines waiting to read from it and ensures that future reads will not block.
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
package main
import (
"sync"
"time"
)
type msg struct {
content string
done chan struct{}
}
func (m *msg) Init() {
time.Sleep(time.Second) // Simulate a long-running process.
m.content = "Hello, world!"
close(m.done)
}
func newMsg() *msg {
return &msg{"", make(chan struct{})}
}
func main() {
m := newMsg()
var wg sync.WaitGroup
go m.Init()
for range 5 {
wg.Add(1)
go func() {
defer wg.Done()
<-m.done
println("msg:", m.content)
}()
}
wg.Wait() // Blocks; waits for the goroutines above to finish.
<-m.done // Does not block; m.done is already closed.
}
This is the same mechanism used by the context package to signal to all downstream functions that a context has been canceled.