From fb350e7f0f317d7ebcb4d98f7929e7e0781dc537 Mon Sep 17 00:00:00 2001 From: 7leap <112567966+wureny@users.noreply.github.com> Date: Wed, 13 Dec 2023 10:28:43 +0800 Subject: [PATCH] Write summary for mistake 76 --- docs/index.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/docs/index.md b/docs/index.md index d3825c3..f947da9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1756,6 +1756,68 @@ ticker = time.NewTicker(1000 * time.Nanosecond) ???+ info "TL;DR" Avoiding calls to `time.After` in repeated functions (such as loops or HTTP handlers) can avoid peak memory consumption. The resources created by `time.After` are released only when the timer expires. +People often use time.After in loops or HTTP handlers repeatedly to implement the timing function. But it can lead to unintended peak memory consumption due to the delayed release of resources, just like the following code: + +```go +func consumer1(ch <-chan Event) { + for { + select { + case event := <-ch: + handle(event) + case <-time.After(time.Hour): + log.Println("warning: no messages received") + } + } +} +``` + +The source code of the function time.After is as follows: + +```go +func After(d Duration) <-chan Time { + return NewTimer(d).C +} +``` + +As we see, it returns receive-only channel. + +When time.After is used in a loop or repeated context, a new channel is created in each iteration. If these channels are not properly closed or if their associated timers are not stopped, they can accumulate and consume memory. The resources associated with each timer and channel are only released when the timer expires or the channel is closed. + +To avoid this happening, We can use context's timeout setting instead of time.After, like below: + +```go +func consumer2(ch <-chan Event) { + for { + ctx, cancel := context.WithTimeout(context.Background(), time.Hour) + select { + case event := <-ch: + cancel() + handle(event) + case <-ctx.Done(): + log.Println("warning: no messages received") + } + } +} +``` + +We can also use time.NewTimer like below: + +```go +func consumer3(ch <-chan Event) { + timerDuration := 1 * time.Hour + timer := time.NewTimer(timerDuration) + + for { + timer.Reset(timerDuration) + select { + case event := <-ch: + handle(event) + case <-timer.C: + log.Println("warning: no messages received") + } + } +} +``` [Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/10-standard-lib/76-time-after/main.go)