Swift并发模型中的Task

在 Swift并发中:

Task 是一种用于启动、管理和控制异步操作的轻量级结构。

它不是线程,而是运行在 Swift 并发运行时中的“协程任务”。

基本功能

1、运行异步任务:

Task {

await doSomething()

}

会自动在适当的线程/调度器上运行,如果在同步函数里,想调用 async 函数,就要用 Task 包裹。

2、支持取消(可取消点)

Task 可以被取消,当它在某些挂起点(如 Task.sleep, await 网络请求)时,会主动感知被取消。

3、支持切换线程(自动调度)

它可能在不同时间切换到不同线程执行,不需要手动管理。

初始化方式

1、非结构化任务

Task {

await doWork()

}

任务不受父任务约束,在全局执行,不会自动随View视图生命周期取消。

2、结构化任务(子任务)

await withTaskGroup(of: Void.self) { group in

group.addTask {

await work()

}

}

父任务结束时,所有子任务也取消。

3、MainActor任务(保证主线程)

Task { @MainActor in

self.value = 42

}

适合更新 UI。

4、Detached Task(完全独立任务)

Task.detached {

await perform()

}

Task.detached 启动的任务是脱离当前上下文(actor/优先级/结构化任务)的独立任务。

等同于:

Task.detached(priority: nil, operation: { ... })

指定优先级:

Task.detached(priority: .background) {

await loadInBackground()

}

不继承当前 actor 上下文(所以不能操作 UI,除非加 @MainActor)。

完全独立,不受父任务影响,适合用来执行独立的后台任务。

常用方法

Task 在 Swift Concurrency 中不仅是用来创建异步任务的,还自带一套方法和属性,用来管理任务的生命周期、状态、优先级、取消等。

1、Task保存到变量

var tradeTask: Task? // 保存任务的变量

tradeTask = Task {

await executeTrade()

}

2、设置优先级:

Task(priority: .high) {

await fetchData()

}

可用的优先级有:

TaskPriority:

- .high

- .medium ← 默认

- .low

- .background

- .utility

- .userInitiated

系统会根据优先级优化调度顺序,但不是绝对保证执行顺序。

3、取消任务:

let task = Task {

try await longRunningTask()

}

task.cancel()

cancel() 会发送取消信号,任务是否真正终止,取决于任务内部是否响应这个取消请求。

4、响应取消任务:

在取消任务时,还需要在任务内部响应取消,使用 Task.isCancelled 或 try Task.checkCancellation()。

func longRunningTask() async throws {

for i in 0..<100 {

try Task.checkCancellation()

// 做某件事

}

}

Task.checkCancellation()用来检测是否已经被取消(Task.isCancelled == true),然后手动抛出 CancellationError,提前中止任务执行。

5、isCancelled:检查当前任务是否被取消(用于条件判断)。

if Task.isCancelled {

print("已被取消")

}

Task.isCancelled 是 Swift Concurrency 中的一个静态属性,用于检测当前任务是否已经被取消。

当前异步任务已经收到取消请求,返回true。

当前任务还在正常执行,返回false。

6、获取结果(Task 是泛型):

let task = Task { () -> String in

return await fetchName()

}

let result = await task.value // 等待并获取结果

Task 是个泛型类型,可以从 .value 中获得最终返回值,更多内容请见《Swift并发模型中的Task.result属性》。

7、currentPriority:获取当前任务的优先级。

8、yield():暂时让出执行权,等待其他任务运行(任务调度优化)。

await Task.yield()

这在长时间运行的任务中,可以让系统“插个队”,提高响应性。

8、Task.sleep():暂停当前任务一段时间,属于 Task 类型的静态方法。

try await Task.sleep(nanoseconds: 1_000_000_000) // 暂停 1 秒

挂起期间线程是空闲的,可以执行别的任务,更多内容请见《Swift并发Task.sleep》。

保存Task

Task本身是一个泛型类型:

Task

where Failure : Error

Task有两个属性:

1、Success:成功时返回的类型。

2、Failure:失败时抛出的错误类型(必须是 Error 子类)。

例如:

Task {

return 3

}

这段代码表示:最终会产生一个Int,可能抛出错误Erro。

如果Task没有返回内容,不会抛出报错,可以使用Void和Never类型创建保存Task的变量:

var tradeTask: Task?

保存Task变量后,可以随时使用 cancel() 方法取消任务:

tradeTask = Task {

try await longRunningTask()

}

task.cancel()

总结

Task主要用于运行async异步任务。

async函数是“可挂起的函数”,Task是可以执行async函数的实体。

相关文章

1、Swift并发模型中的Task.result属性:https://fangjunyu.com/2024/12/09/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84task-result%e5%b1%9e%e6%80%a7/

2、Swift主线程:https://fangjunyu.com/2024/12/25/swift%e4%b8%bb%e7%ba%bf%e7%a8%8b/

3、Swift并发Task.sleep:https://fangjunyu.com/2025/01/10/swiftui%e5%bb%b6%e8%bf%9f%e6%93%8d%e4%bd%9csleep/

4、Swift并发模型中的Task.result属性:https://fangjunyu.com/2024/12/09/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84task-result%e5%b1%9e%e6%80%a7/

5、Swift并发模型中的TaskGroup和async let:https://fangjunyu.com/2025/05/15/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84taskgroup%e5%92%8casync-let/

扩展知识

Task与主线程的问题

Swift Concurrency 中关于 Task 是否在主线程、何时进入后台线程,很多人一开始都会有些模糊,我们来清晰梳理:

1、Task {} 会继承当前上下文(包括线程)

// 在主线程中调用

Task {

print(Thread.isMainThread) // true

await someAsyncWork()

print(Thread.isMainThread) // true(可能)

}

这是结构化任务,会尽量继承当前的 actor(包括主线程 MainActor)。

但注意:await 后恢复的线程可能不是主线程!除非用 @MainActor 限定。

注:Tread.isMainThread 是 Swift 提供的一个静态属性,用于判断 当前代码是否运行在主线程上,具体请见《Swift主线程》底部的扩展知识。

2、Task.detached {} 不继承主线程上下文

Task.detached {

print(Thread.isMainThread) // false(通常在后台)

await someAsyncWork()

}

这是“非结构化任务”,不会继承当前线程、actor 或任务优先级。

更适合用来处理不依赖 UI 的独立任务(比如下载图片、解析大数据)。

3、async/await 会换线程吗?

可能会换线程。

Task {

print("before await:", Thread.isMainThread) // true

await doSomethingAsync()

print("after await:", Thread.isMainThread) // true or false(不确定)

}

await 会挂起当前任务,让出线程。

Swift 会在“可用线程池”中恢复这个任务。

恢复时不保证还是原来的线程,除非绑定到 @MainActor。

4、如何强制保持在主线程?

@MainActor

func updateUI() {

// 一定在主线程

}

Task {

await updateUI() // 强制主线程

}

或者用 MainActor.run 块:

Task {

await MainActor.run {

// 在主线程中更新 UI

}

}

还有一种常见的写法:

Task {

@MainActor in

// 这里的代码会在主线程(MainActor)上运行

}

@MainActor in 和 await MainAcotr.run是等价的,@MainActor是更简洁的语法糖。

5、Task{}可以在后台线程创建么?

Task {} 可以在后台线程中创建,而且这样做时,它会继承那个后台线程的上下文,不会自动“跳回主线程”。

代码示例:

DispatchQueue.global().async {

print("现在在哪?", Thread.isMainThread) // false

Task {

print("Task 在哪?", Thread.isMainThread) // false 仍在后台

await doSomething()

}

}

友情链接:
Copyright © 2022 暴击魔方福利站 All Rights Reserved.