Mostly your solution you posted below is as good as it can get. Couple of tips to improve it:
- Alternatively you may close the channel to signal completion instead of sending a value on it, a receive operation on a closed channel can always proceed immediately.
- And it's better to use
defer
statement to signal completion, it is executed even if a function terminates abruptly.
- Also if there is only one "job" to wait for, you can completely omit the
WaitGroup
and just send a value or close the channel when job is complete (the same channel you use in your select
statement).
- Specifying 1 second duration is as simple as:
timeout := time.Second
. Specifying 2 seconds for example is: timeout := 2 * time.Second
. You don't need the conversion, time.Second
is already of type time.Duration
, multiplying it with an untyped constant like 2
will also yield a value of type time.Duration
.
I would also create a helper / utility function wrapping this functionality. Note that WaitGroup
must be passed as a pointer else the copy will not get "notified" of the WaitGroup.Done()
calls. Something like:
// waitTimeout waits for the waitgroup for the specified max timeout.
// Returns true if waiting timed out.
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
c := make(chan struct{})
go func() {
defer close(c)
wg.Wait()
}()
select {
case <-c:
return false // completed normally
case <-time.After(timeout):
return true // timed out
}
}
Using it:
if waitTimeout(&wg, time.Second) {
fmt.Println("Timed out waiting for wait group")
} else {
fmt.Println("Wait group finished")
}
Try it on the Go Playground.