如何从GCD(DispatchQueue)转换为Swift async/await?

gj3fmq9x  于 2023-02-03  发布在  Swift
关注(0)|答案(1)|浏览(132)

我正在关注斯坦福大学的CS193p开发iOS应用程序在线课程。
它正在使用Grand Central Dispatch(GCD)API进行多线程演示。
“截至WWDC 2021,GCD已基本被Swift新的内置异步API所取代”。
所以我想了解一下,在更新了课程中的代码以使用这个新的API之后,它看起来会是什么样子。
看过苹果的WWDC视频后,我觉得
在这个新的异步API中,DispatchQueue.global(qos: .userInitiated).async { }被替换为Task { }Task(priority: .userInitiated) {},但我不确定,DispatchQueue.main.async { }被替换为什么?

所以我的问题是

1.我的假设是否正确,DispatchQueue.global(qos: .userInitiated).async { }已替换为Task(priority: .userInitiated) {}

  1. DispatchQueue.main.async { }已被替换为什么?
    请帮助,我想学习这个新的异步等待API。
    以下是讲座中的代码,使用的是旧的GCD API:
DispatchQueue.global(qos: .userInitiated).async {
    let imageData = try? Data(contentsOf: url)
    DispatchQueue.main.async { [weak self] in
        if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
            self?.backgroundImageFetchStatus = .idle
            if imageData != nil {
                self?.backgroundImage = UIImage(data: imageData!)
            }
            // L12 note failure if we couldn't load background image
            if self?.backgroundImage == nil {
                self?.backgroundImageFetchStatus = .failed(url)
            }
        }
    }
}

整个函数(如果您需要查看更多代码):

private func fetchBackgroundImageDataIfNecessary() {
    backgroundImage = nil
    switch emojiArt.background {
    case .url(let url):
        // fetch the url
        backgroundImageFetchStatus = .fetching
        DispatchQueue.global(qos: .userInitiated).async {
            let imageData = try? Data(contentsOf: url)
            DispatchQueue.main.async { [weak self] in
                if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
                    self?.backgroundImageFetchStatus = .idle
                    if imageData != nil {
                        self?.backgroundImage = UIImage(data: imageData!)
                    }
                    // L12 note failure if we couldn't load background image
                    if self?.backgroundImage == nil {
                        self?.backgroundImageFetchStatus = .failed(url)
                    }
                }
            }
        }
    case .imageData(let data):
        backgroundImage = UIImage(data: data)
    case .blank:
        break
    }
}
njthzxwz

njthzxwz1#

如果你真的要做一些缓慢而同步的事情,Task.detached更接近于GCD向全局队列的分派,如果你只是使用Task(priority: ...) { ... },你就把它留给并发系统来决定在哪个线程上运行它(仅仅因为你指定了一个较低的priority并不能保证它不会在主线程上运行)。
例如:

func fetchAndUpdateUI(from url: URL) {
    Task.detached {                          // or specify a priority with `Task.detached(priority: .background)`
        let data = try Data(contentsOf: url)
        let image = UIImage(data: data)
        await self.updateUI(with: image)
    }
}

如果您想在主线程上执行UI更新,而不是将其调度回主队列,则只需将@MainActor修饰符添加到更新UI的方法中:

@MainActor
func updateUI(with image: UIImage?) async {
    imageView.image = image
}

尽管如此,这是一个非常不寻常的模式(同步处理网络请求并创建一个分离的任务以确保不会阻塞主线程)。我们可能会使用URLSession的新异步方法data(from:delegate:)来异步执行请求。它提供了更好的错误处理,更好的可配置性,参与结构化并发,并且是可取消的。
简而言之,不要为旧的GCD模式寻找一对一的类似物,而是尽可能使用Apple提供的并发API。
FWIW,除了上面显示的@MainActor模式(作为分派到主队列的替代),您还可以执行以下操作:

await MainActor.run {
    …
}

这大致类似于向主队列的调度。在WWDC 2021视频Swift concurrency: Update a sample app中,他们说:
在Swift的并发模型中,有一个全局参与者叫做main actor,它负责协调主线程上的所有操作,我们可以把我们的DispatchQueue.main.async替换成MainActorrun函数调用,这需要在MainActor上运行一段代码......
但他接着说:
我可以用@MainActor注解函数,这要求调用者在函数运行之前切换到主参与者。......现在我们已经将此函数放在主参与者上,严格地说,我们不再需要MainActor.run

相关问题