Swift 异步与并发

当你调用一个同步函数时,你的线程是阻塞的,等待函数完成,在它完成之前,你的线程不能做其他任何事情。调用一个异步操作后,当前线程不会被阻塞。异步不一定是并发。并发是在同一时间段内有多个任务同时推进(通过多线程交替或多核并行实现)。

我们来看异步的例子。

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) 验证。

参考资料

​认识 Swift 中的 async/await - WWDC21 - 视频 - Apple Developer

相关推荐
安当加密03017 小时前
PCI DSS 4.0与SWIFT CSP双合规:银行收单与跨境支付HSM部署实战
swift·hsm·密钥管理·pci dss·双合规
麒麟马1 天前
Swift 扩展归纳---Array (一)
ios·swift
2501_915106321 天前
Swift 4开发环境设置教程:xCode安装与Playground入门
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
东坡肘子2 天前
当 Mac mini 的价格不再 mini -- 肘子的 Swift 周报 #152
人工智能·swiftui·swift
2501_915106325 天前
第一次开发 iPhone App,可能碰到的问题,解决办法
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
末代iOS程序员华仔6 天前
iOS 5.6 条例解决方法
flutter·ios·swift
末代iOS程序员华仔6 天前
Guideline 5.6 - Developer Code of Conduct
flutter·ios·swift
sakiko_8 天前
OC基础语法(与Swift对比)-1
开发语言·ios·objective-c·swift
2501_916008898 天前
Flutter 开发 iOS,项目打包成 IPA,命令与免 Xcode两种方法
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程