当你调用一个同步函数时,你的线程是阻塞的,等待函数完成,在它完成之前,你的线程不能做其他任何事情。调用一个异步操作后,当前线程不会被阻塞。异步不一定是并发。并发是在同一时间段内有多个任务同时推进(通过多线程交替或多核并行实现)。
我们来看异步的例子。
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
DispatchQueue.main.async {
print("1")
}
print("2")
}
}
ViewController 是 MainActor 隔离的,buttonTapped(:) 也是 MainActor 隔离的,即运行在主线程。buttonTapped(:) 把闭包放入队列,继续执行 print("2")。闭包执行是异步的,但不是并发的,闭包必须等待主线程上的任务全部执行完毕、RunLoop 进入下一轮循环后,才会被主线程按顺序执行。
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
foo()
}
func foo() {
Task {
print("1")
}
print("2")
}
}
由于方法 foo() 是 MainActor 隔离的,Task 默认与 foo() 有相同的 actor 隔离,因此 Task 的闭包也是 MainActor 隔离的。建立 Task 后,不阻塞主线程,继续执行 print("2")。Task 的闭包与 foo() 方法异步执行,都在主线程,但不是并发。
如果方法 foo() 是非 actor 隔离的,则 Task 也是非 actor 隔离的。
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
foo()
}
nonisolated func foo() {
Task {
print("1")
}
print("2")
}
}
foo() 方法在主线程被调用,运行在主线程,Task 的闭包运行在非主线程,异步,并且是并发。
即使 foo() 是 MainActor 隔离的,如果调用 Task.detached,Task 的闭包没有任何 actor 隔离,运行在非主线程,异步,并且是并发。
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
foo()
}
func foo() {
Task.detached {
print("1")
}
print("2")
}
}
再看看异步函数,我们把 foo() 改为异步,buttonTapped(_:) 是同步方法,需要一种方式由同步进入异步。接决方案是前面的 Task。
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
Task {
print("1")
await foo()
print("2")
}
}
func foo() async {
print("3")
}
}
在 Task 的闭包中调用 foo(),buttonTapped(_:) 是 MainActor 隔离的,Task 也是MainActor 隔离的,Task 闭包运行在主线程。print("1") 运行在主线程,await foo() 使闭包挂起,系统会使用主线程来做其他工作,foo() 也是 MainActor 隔离的,在主线程执行 foo(),foo() 返回,接着在主线程恢复运行 print("2")。foo() 和 Task 闭包是异步的,但不并发。
如果使 foo() 非 actor 隔离,
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
Task {
print("1")
await foo()
print("2")
}
}
nonisolated func foo() async {
print("3")
}
}
与上面的唯一的区别是 foo() 运行在别的线程。foo() 和 Task 闭包是异步的,并且是并发。 如果 Task 闭包非 actor 隔离,
swift
class ViewController: UIViewController {
@IBAction func buttonTapped(_ sender: UIButton) {
Task.detached {
print("1")
await self.foo()
print("2")
}
}
nonisolated func foo() async {
print("3")
}
}
Task 闭包中 print("1") 在非主线程执行,await self.foo() 使闭包挂起,系统会使用当前线程来做其他工作,foo() 运行在别的线程,返回,闭包恢复执行 print("2"),但线程可能与 print("1") 线程不同。foo() 和 Task 闭包是异步的,并且是并发。
如果异步方法 foo() 内部调用另一个异步函数,则分析过程与 Task 闭包调用 foo() 类似。
异步函数与前面的异步不同,调用异步函数会使当前线程挂起,系统会使用当前线程来做其他工作。相同点是都不会阻塞当前线程,而同步会阻塞当前线程。
以上代码在Xcode26.6 (Swift version 6.3.3) 验证。
参考资料