一、两句话区分
API | 阻塞谁 | 后果 |
---|---|---|
Thread.sleep(forTimeInterval:) |
整条线程 | 线程池"饿死",其他任务无法调度 |
Task.sleep(nanoseconds:) |
当前任务 | 线程立刻转去跑别的任务,资源不浪费 |
结论:
Swift Concurrency 时代,永远用
Task.sleep
,不要用Thread.sleep
。
二、为什么 Thread.sleep 这么毒?
-
线程池大小固定
Swift 并发运行时默认只开 CPU 核心数条线程(M1 ≈ 8)。
-
你睡一条,就少一条
Thread.sleep
让线程进入内核阻塞状态,不会被运行时回收。 -
四条全睡 → App 卡死
示例:
swift
for _ in 0..<4 {
Task {
Thread.sleep(forTimeInterval: 10)
} // 4 条线程瞬间用完
}
此时所有 Task(网络、UI、动画)都排不上队,应用假死 10 秒。
三、Task.sleep 是怎么做到"不堵线程"的?
swift
await try Task.sleep(nanoseconds: 1_000_000_000) // 1 秒
内部流程:
- 当前任务被挂起 → 让出线程
- 运行时把线程分配给其他待执行任务
- 1 秒后,原任务重新入队 → 任意空闲线程继续执行
→ 零线程浪费,零阻塞,零内核调用(用户态挂起)。
四、代码对比:同样"等 1 秒",效果天差地别
Thread.sleep 版(灾难)
swift
Task {
print("start", Date())
Thread.sleep(forTimeInterval: 1) // 阻塞整条线程
print("end ", Date())
}
Task.sleep 版(安全)
swift
Task {
print("start", Date())
await try Task.sleep(nanoseconds: 1_000_000_000) // 让出线程
print("end ", Date())
}
并行 10 个任务:
Thread.sleep
→ 10 秒总耗时(串行)Task.sleep
→ 约 1 秒全部完成(并发)
五、常见踩坑 QA
❓ "我就想在 Playground 里拖延一下,也不能 Thread.sleep
?"
→ 用 Task.sleep
一样简单:
swift
Task {
await try Task.sleep(nanoseconds: 2_000_000_000)
print("done")
}
❓ "需要主线程延迟,用哪个?"
→ 依旧 Task.sleep
,它会在任意线程醒来,若需主线程再切回来:
swift
await MainActor.run {
// 主线程工作
}
❓ "老代码里大量 Thread.sleep
怎么批量替换?"
→ 正则 + 脚本一键迁移:
bash
# 示例:sed -i 's/Thread.sleep(\([^)]*\))/await Task.sleep(UInt64(\1 * 1_000_000_000))/g' *.swift
六、一句话总结
"睡线程"是毒药,"睡任务"才是解药。
记住:
Swift Concurrency 世界里,看到 Thread.sleep
就改成 Task.sleep
------没有任何例外。