diff --git a/README.md b/README.md index ea7a679..33e2be3 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ Go tips from [Phuong Le](https://twitter.com/func25). | 60 | sync.Once is the best way to do things once | smallnest | | 61 | | | | 62 | | | -| 63 | | | +| 63 | Avoid time.Sleep(), it's not context-aware and can't be interrupted | richzw | | 64 | | | | 65 | Returning Pointers Made Easy with Generics | miniLCT | | 66 | Simplify Your Error Messages in fmt.Errorf | smallnest | diff --git a/src/063.md b/src/063.md new file mode 100644 index 0000000..d014621 --- /dev/null +++ b/src/063.md @@ -0,0 +1,58 @@ +# Tip #63 避免使用time.Sleep(),它不能被context感知且无法被中断 + +> 原始链接:[ Golang Tip Tip #63: Avoid time.Sleep(), it's not context-aware and can't be interrupted.](https://twitter.com/func25/status/1773701845267153348) +> + +使用 `time.Sleep` 可能看起来很有用,但它不能被context 感知,也无法被中断。 + +例如,如果我们的应用程序正在关闭,我们无法向正在休眠的函数发送信号。一旦它醒来,它会开始执行一些逻辑,但在工作时可能会被中断。 + +我自己也犯过这个错误,并将其作为学习的一点: + +![](./images/063/001.png) + +这些循环执行一些工作,然后使用 `time.Sleep` 暂停 5 秒钟,然后继续执行。 + +但是,这些循环无法通过context cancel来停止,如果我们需要快速停止一切,这是一个问题。 + +让我们传递context以使我们的工作考虑到context cancel: + +![](./images/063/002.png) + +这样做更好一些;虽然有点冗长,但我们的工作现在尊重上下文,例如: + +- 1. -> doWork -> sleep -> shutdown -> ctx.Done() -> 结束。 +- 2. -> doWork -> shutdown -> sleep -> ctx.Done() -> 结束。 + +(我们可以将 `doWork(ctx)` 放在 `for` 循环的正下方,以减少嵌套。) + +但是,我们仍然需要等待 5 秒钟,或者根据作业延迟的情况可能更长。 + +**使用time包作为信号** + +现在,我们正在讨论的解决方案使用了time包,但有一些微妙之处。 + +第一个修复涉及 `time.After`,在循环中如下所示: + +![](./images/063/003.png) + +这种方法简单而直接,但并不完美。 + +- 我们每次都在分配一个新的channel。 +- Go 社区指出,`time.After` 可能会导致短期内存泄漏。 + +如果我们的函数在倒计时之前因为 `ctx.Done()` 而结束,那么 `time.After` 将一直存在,直到时间到期。 + +第二个修复可能更冗长,但旨在解决这个问题: + +![](./images/063/004.png) + +我们设置了一个计时器,当它到期时,我们重新启动它。 + +当context完成时,重要的是我们停止计时器以避免泄漏,可以使用与上面相同的解决方案,或者使用 `defer delay.Stop()`。 + +**最后的解决方案并不完美** + +将我们的工作直接放在 `for` 循环下面可以减少代码的嵌套。但这并不能确保计时器第一次就是正确的。 + +您可能希望将我们预期的逻辑放在 `'case <- delay.C'` 下面。 diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 9efe652..7c3257f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -22,6 +22,7 @@ - [Tip #50 使结构体不可比较](./050.md) - [Tip #60 sync.Once是执行单次操作的最佳方式](./060.md) - [Tip #71 用泛型让 sync.Pool 类型安全](./071.md) +- [Tip #63 避免使用time.Sleep(),它不能被context感知且无法被中断](./063.md) - [Tip #65 使用泛型返回指针](./065.md) - [Tip #66 在fmt.Errorf中简化你的错误信息](./066.md) - [Tip #67 如何处理长函数签名](./067.md) diff --git a/src/images/063/001.png b/src/images/063/001.png new file mode 100644 index 0000000..ce46a6d Binary files /dev/null and b/src/images/063/001.png differ diff --git a/src/images/063/002.png b/src/images/063/002.png new file mode 100644 index 0000000..cecd9c0 Binary files /dev/null and b/src/images/063/002.png differ diff --git a/src/images/063/003.png b/src/images/063/003.png new file mode 100644 index 0000000..ddb76c0 Binary files /dev/null and b/src/images/063/003.png differ diff --git a/src/images/063/004.png b/src/images/063/004.png new file mode 100644 index 0000000..f969370 Binary files /dev/null and b/src/images/063/004.png differ