Goroutine: time.Sleep or time.After
Per go101
The two will both pause the current goroutine execution for a certain duration. The difference is the function call
time.Sleep(d)
will let the current goroutine enter sleeping sub-state, but still stay in running state, whereas, the channel receive operation<-time.After(d)
will let the current goroutine enter blocking state.
Update 08/15/2022
The difference from go dev
- time.After
After waits for the duration to elapse and then sends the current time on the returned channel.
It is equivalent to `NewTimer(d).C`.
The underlying Timer is not recovered by the garbage collector
until the timer fires. If efficiency is a concern, use `NewTimer`
instead and call `Timer.Stop` if the timer is no longer needed.
- time.Sleep
Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
As @cnicutar pointed out that
After is useful in contexts where one already needs to select on a number of channels but would also like a timeout
Here is another details example to compare time.After
with time.Sleep
within select
var wg sync.WaitGroup
func cancelAfter(interval time.Duration, cancel context.CancelFunc) {
go func(cancel context.CancelFunc) {
time.Sleep(interval)
fmt.Println("cancel task at ", time.Now())
cancel()
wg.Done()
}(cancel)
}
func TestTimerSleep(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
cancelAfter(time.Second*15, cancel)
wg.Add(1)
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("TimerSleep is cancelled", time.Now())
wg.Done()
return
default:
time.Sleep(time.Second * 6)
fmt.Println("time sleep", time.Now())
}
}
}(ctx)
wg.Wait()
}
func TestTimerAfter(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
cancelAfter(time.Second*15, cancel)
wg.Add(1)
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("TimerAfter is cancelled", time.Now())
wg.Done()
return
case <-time.After(time.Second * 6):
fmt.Println("time after", time.Now())
}
}
}(ctx)
wg.Wait()
}
Results
TestTimerSleep
time sleep 2022-08-15 15:43:56.502134
time sleep 2022-08-15 15:44:02.502682
cancel task at 2022-08-15 15:44:05.501721
time sleep 2022-08-15 15:44:08.503892
TimerSleep is cancelled 2022-08-15 15:44:08.50393
TestTimerAfter
time after 2022-08-15 15:44:55.089277
time after 2022-08-15 15:45:01.0905
cancel task at 2022-08-15 15:45:04.089352
TimerAfter is cancelled 2022-08-15 15:45:04.089432
From the results, the time.After
could be canceled immediately when time out.
I don't think it matters much for the majority of programs. There has been a question on golang-nuts about this but I don't think one can draw any conclusion.
In practice After
is useful in contexts where one already needs to select
on a number of channels but would also like a timeout:
select {
case c := <-someChan:
..
case c := <-otherChan:
..
case <-time.After(time.Second * 42):
}
By superficially looking at the code Sleep
seems simpler while After
builds a new timer, with a period, a closure to send the time when it finishes etc.
Again, I don't think it matters in practice but time.Sleep
seems pretty readable so I would go with that.
On my implementation both of them perform the exact same system calls and end up waiting:
futex(??, FUTEX_WAIT, 0, {41, 999892351}
^^ 41 seconds and change