Background:
From the very beginning, the Go language has provided the time package, which contains fundamental functions for time management — time.Sleep and time.After. Unlike languages with system sleep (System.sleep), Go implements asynchronous timers through its primitives, which is important for multithreaded work.
Issue:
Often developers misuse time.Sleep to implement pauses between tasks, which is undesirable in concurrent programs. In the Go paradigm, it is more appropriate to manage the waiting for events through channels and time.After for integration with select/channels.
Solution:
time.Sleep(d) blocks the current goroutine for d duration, this is a direct 'sleep'. time.After(d) returns a channel on which an event-time will appear after d. The latter option is much more flexible in select for channels, convenient for interruptible waits and timeouts.
Code example:
ch := make(chan struct{}) go func() { time.Sleep(2 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("done") case <-time.After(1 * time.Second): fmt.Println("timeout") }
Key features:
Can time.Sleep be used to block the execution of the entire program?
No, time.Sleep only 'sleeps' one goroutine; the others continue to work.
Can time.After lead to memory leaks if the channel is not read?
Yes, the timer hangs until the value is read — if there is no reading, the garbage collector will not delete the object.
Code example:
func leak() { for { _ = time.After(time.Hour) } }
How to correctly cancel the waiting on time.After if no event is received?
It is better to use time.Timer and manually stop it if you need to finish waiting before the deadline:
t := time.NewTimer(time.Minute) if done { t.Stop() }
A goroutine waits for a signal from work, and in case of a timeout does this:
time.Sleep(10*time.Second) doSomething()
Pros:
Cons:
The code is structured using select and time.After:
select { case <-workSignal: // Execute case <-time.After(10 * time.Second): // Do by timeout }
Pros:
Cons: