烽火村游戏活动中心

HOME> 玩家社区> Swift Concurrency:彻底告别“线程思维”,拥抱 Task 的世界

Swift Concurrency:彻底告别“线程思维”,拥抱 Task 的世界

2026-06-15 15:45:23

原文:Threads vs. Tasks in Swift Concurrency 链接:www.avanderlee.com/concurrency...

前言:别再问"它跑在哪个线程?"

在 GCD 时代,我们习惯用 DispatchQueue.global(qos: .background).async { ... } 或 DispatchQueue.main.async { ... } 来显式地把任务丢到指定线程。久而久之,形成了一种"线程思维":

"这段代码很重,我要放到子线程。"

"这行 UI 代码必须回到主线程。"

Swift Concurrency(async/await + Task)出现以后,这套思维需要升级------系统帮你决定"跑在哪个线程"。我们只需关心"任务(Task)"本身。

线程(Thread)到底是什么?

系统级资源:由操作系统调度,创建、销毁、切换开销大。

并发手段:多线程可以让多条指令流同时跑。

痛点:数量一多,内存占用高、上下文切换频繁、优先级反转。

Swift Concurrency 的目标就是让我们 不再直接面对线程。

Task:比线程更高级的抽象

一个 Task = 一段异步工作单元。

不绑定线程:Task 被放进 合作线程池(cooperative thread pool),由运行时动态分配到"刚好够用"的线程上。

运行机制:

线程数量 ≈ CPU 核心数。

遇到 await(挂起点)时,当前线程被释放,可立即执行其他 Task。

挂起的 Task 稍后可能在另一条线程恢复。

代码示范:Task 与线程的"若即若离"

swift

复制代码

struct ThreadingDemonstrator {

private func firstTask() async throws {

print("Task 1 started on thread: \(Thread.current)")

try await Task.sleep(for: .seconds(2)) // 挂起点

print("Task 1 resumed on thread: \(Thread.current)")

}

private func secondTask() async {

print("Task 2 started on thread: \(Thread.current)")

}

func demonstrate() {

Task {

try await firstTask()

}

Task {

await secondTask()

}

}

}

典型输出(每次都可能不同):

arduino

复制代码

Task 1 started on thread: {number = 3, name = (null)}

Task 2 started on thread: {number = 8, name = (null)}

Task 1 resumed on thread: {number = 7, name = (null)}

解读:

Task 1 在 await 时释放了线程 3;

Task 2 趁机用到了线程 8;

Task 1 恢复时,被安排到线程 7------前后线程可以不同。

线程爆炸(Thread Explosion)还会发生吗?

场景

GCD

Swift Concurrency

同时发起 1000 个网络请求

可能创建 1000 条线程 → 内存暴涨、调度爆炸

最多 CPU 核心数条线程,其余任务挂起 → 无爆炸

阻塞线程

线程真被 block,CPU 空转

用 continuation 挂起,线程立刻服务别的任务

因此,线程爆炸在 Swift Concurrency 中几乎不存在。

线程更少,性能反而更好?

GCD 误区:线程越多,并发越高。

真相:线程 > CPU 核心时,上下文切换成本激增。

Swift Concurrency 做法 :

线程数 = 核心数;

用挂起/恢复代替阻塞;

CPU 始终在跑有效指令,切换开销极低。

实测常见场景(CPU-bound & I/O-bound)下,Swift Concurrency 往往优于 GCD。

三个常见误区

误区

正解

每个 Task 会新开一条线程

Task 与线程是多对一,由调度器动态复用

await会阻塞当前线程

await会挂起任务并释放线程

Task 一定按创建顺序执行

执行顺序不保证,取决于挂起点与调度策略

思维升级:从"线程思维"到"任务思维"

线程思维

任务思维

"这段代码要在子线程跑"

"这段代码是异步任务,系统会调度"

"回到主线程刷新 UI"

"用 @MainActor或 MainActor.run标记主界面任务"

"我怕线程太多"

"线程数系统自动管理,我专注业务逻辑"

小结

线程是低层、昂贵的系统资源。

Task 是高层、轻量的异步工作单元。

Swift Concurrency 通过合作线程池 + 挂起/恢复机制,让线程数始终保持在"刚好够用",既避免线程爆炸,又提升性能。

开发者应把注意力从"线程"转向"任务"与"挂起点"。

当你下次再想问"这段代码跑在哪个线程?"时,提醒自己:

"别管线程,写正确的 Task 就行。"

最新发表
友情链接