我正在努力学习快速并发,但它带来了很多困惑.据我所知,任务{}是一个异步单元,它将允许我们从同步上下文中桥接异步函数调用.它类似于DispatchQueue.Global(),后者将在某个任意线程上执行该块.

override func viewDidLoad() {
        super.viewDidLoad()
        
        Task {
            do {
                    
                 let data = try await asychronousApiCall()
                 print(data)
                    
                } catch {
                    print("Request failed with error: \(error)")
                }
        }
        
        for i in 1...30000 {
            print("Thread \(Thread.current)")
        }
    }

下面是我的AsychronousApiCall函数

func asychronousApiCall() async throws -> Data {
        print("starting with asychronousApiCall")
        print("Thread \(Thread.current)")
        let url = URL(string: "https://www.stackoverflow.com")!
        // Use the async variant of URLSession to fetch data
        // Code might suspend here
        let (data, _) = try await URLSession.shared.data(from: url)
        
        return data
    }

当我try 这个实现时.我总是看到,在for loop完成之后,打印出"Start with asychronousApiCall",线程是MainThread.

就像这样

Thread <_NSMainThread: 0x600000f10500>{number = 1, name = main}

推荐答案

你说过:

我知道Task {}是一个异步单元,它将允许我们从同步上下文中桥接async函数调用.

是.

您将继续:

它类似于DispatchQueue.global(),后者将在某个任意线程上执行该块.

不,如果你从主要演员那里叫它,它更接近于DispatchQueue.main.async { … }.正如documentation所说的,它"代表current参与者将给定的非抛出操作作为新的顶级任务的一部分进行异步运行"[强调添加].也就是说,如果您当前使用的是主要参与者,则该任务也将代表主要参与者运行.

虽然纠结于直接GCD到并发的映射可能是错误的,但Task.detached { … }DispatchQueue.global().async { … }更接近.

你 comments 说:

请滚动到the article页最后的figure 8页.它有一个正常的任务,而线程是打印的,是另一个线程.

figure 8 figure 8

在该屏幕快照中,他们显示在挂起点之前(即在await之前),它在主线程上(这是有意义的,因为它代表同一参与者运行它).但他们也强调,在暂停点之后,它在另一个线程上(这看起来可能有悖常理,但这是在暂停点之后可能发生的事情).这是SWIFT并发中非常常见的行为,但可能会有所不同.

顺便说一句,在上面的示例中,您只判断挂起点之前的线程,而不判断挂起点之后的线程.图8的要点是,在挂起点之后使用的线程可能与在挂起点之前使用的线程不同.

如果您有兴趣了解更多有关这些实现细节的信息,我建议您观看WWDC 2021视频Swift concurrency: Behind the scenes.


虽然看Thread.current个很有趣,但应该注意的是,苹果正试图让我们摆脱这种做法.例如,在SWIFT 5.7中,如果我们从异步上下文中查看Thread.current,则会收到警告:

类属性‘Current’在异步上下文中不可用;Thread.Current不能在异步上下文中使用.;这是SWIFT 6中的错误

SWIFT并发性的整体理念是,我们不再考虑线程,而是让SWIFT并发性代表我们 Select 合适的线程(这巧妙地避免了代价高昂的上下文切换;有时会产生在我们预期之外的线程上运行的代码).

Swift相关问答推荐

对多个项目的数组进行排序

SWIFT Vapor控制台应用程序-操作无法完成.权限被拒绝

通过SwiftUI中的列表 Select 从字典中检索值

SWIFT:使字典数组值可变

为什么 Swift URL 的 init?(string: String,relativeTo: URL?) 仅添加协议?

循环字典中的数组

允许视图在视图内更改

带有可选字符串作为键路径的 SwiftUI iOS16 表

使用 swift 的 Firebase 身份验证

覆盖一个子元素的 HStack 对齐方式

Firebase removeObserver 在 Swift 5 中不起作用

如何制作进度加载动画?

Pod lib lint 命令找不到 watchos 模拟器

Reality Composer Tap Trigger 问题

Vapor 4,如何按外键过滤?

如何以快速 Select 器菜单样式调整图像大小

Xcode swift ui 错误KeyPathComparator仅在 macOS 12.0 或更高版本中可用

Swift 2 或 3 中的 Google Analytics 问题

如何在 SwiftUI 中打开 ImagePicker?

枚举大小写的原始值必须是文字