time.sleep仅阻塞当前goroutine而不阻塞整个程序,因其被go调度器视为可让出的阻塞点,触发_g挂起、m切换执行其他就绪_g;参数必须为time.duration类型,需显式指定单位如100*time.millisecond。

time.Sleep 会阻塞当前 goroutine,但不会阻塞整个程序——这是它和系统级 sleep 的本质区别。
为什么 time.Sleep 不会让整个程序卡住
Go 的调度器(GMP 模型)把 time.Sleep 视为一个“可让出”的阻塞点:当前 _G(goroutine)被挂起,M(OS 线程)立刻去执行其他就绪的 _G。所以你写 go fmt.Println("A"); time.Sleep(2 * time.Second); fmt.Println("B"),主线程不会停,其他 goroutine 照常跑。
- 底层调用的是运行时的
park机制,不是系统sleep(2) - 即使只有一条 M(比如
GOMAXPROCS=1),只要还有其他就绪_G,它们仍能被调度 - 但如果所有
_G都在 sleep 或 channel 等待,那 M 就真闲着了
time.Sleep 的参数必须是 time.Duration 类型
不能直接传整数或字符串,常见错误包括:time.Sleep(1000)(缺少单位)、time.Sleep("1s")(类型错)。Go 强制要求显式单位,避免歧义。
- 正确写法:
time.Sleep(100 * time.Millisecond)、time.Sleep(3 * time.Second) -
time.Second是常量,值为1000 * time.Millisecond,本质是int64(纳秒数) - 不要用
time.ParseDuration("1s")做运行时解析——它有 error 返回且慢,编译期已知时间就用字面量
别在 for 循环里无条件调用 time.Sleep
比如轮询检查某个状态:for !done { time.Sleep(100 * time.Millisecond) }。这看似简单,但容易掩盖资源泄漏或逻辑缺陷。
- 更健壮的做法是搭配
context.WithTimeout或select+time.After,方便取消 - 如果只是想“等一会儿再试”,注意:连续 sleep 可能累积误差(尤其在高负载下),
time.Ticker更适合周期性任务 - 测试中遇到 sleep,优先考虑用
testify/mock或接口抽象,而不是真实等待——否则单元测试变慢
真正容易被忽略的,是 sleep 的精度依赖系统定时器分辨率(Windows 通常 15ms,Linux 可达 1ms),以及它无法响应中断——一旦开始 sleep,就只能等到时间到,除非用 context 包裹并配合 select。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











