在后台线程中运行异步函数时遇到问题(并发)

2024-02-05

我在使异步函数在后台线程中运行时遇到问题(以防止阻塞主线程)。

下面是一个运行时间大约为 5 秒的方法。 据我所知,这似乎是使该功能async并将其标记为await在函数调用上就足够了。但它并没有按预期工作,并且用户界面仍然冻结。

EDIT由于据说 Swift 5.5 并发可以取代 DispatchQueue,因此我正在尝试找到一种仅使用 Async/Await 来实现此目的的方法。

EDIT_2我确实尝试删除 @MainActor 包装器,但它似乎仍然在主线程上运行。

NumberManager.swift

@MainActor class NumberManager: ObservableObject {
@Published var numbers: [Double]?    

func generateNumbers() async  {

        var numbers = [Double]()
        numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
        self.numbers = numbers
    // takes about 5 seconds to run...
} }

内容视图

struct ContentView: View {
@StateObject private var numberManager = NumberManager()

var body: some View{
    TabView{
        VStack{
            DetailView(text: isNumbersValid ? "First number is: \(numberManager.numbers![0])" : nil)
                .onAppear() {
                    Task {
                        // Runs in the main thread, freezing up the UI until it completes. 
                        await numberManager.generateNumbers()
                    }
                }

        }
        .tabItem {
            Label("One", systemImage: "list.dash")
        }
        Text("Hello")
            .tabItem {
                Label("Two", systemImage: "square.and.pencil")
            }
    }

}

var isNumbersValid: Bool{
    numberManager.numbers != nil && numberManager.numbers?.count != 0
} }

我尝试过的...

我已经尝试了一些方法,但使其在后台运行的唯一方法是更改​​函数,如下所示。但我知道除非绝对必要,否则应该避免使用 Task.detached,而且我认为这不是正确的用例。

    func generateNumbers() async  {
    Task.detached {
        var numbers = [Double]()
        numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
        await MainActor.run { [numbers] in
            self.numbers = numbers
        }

    }

Writing async在函数上执行不会使其离开线程。你需要一个延续,并且你需要以某种方式真正离开线程。

您可以使用一些方法离开线程DispatchQueue.global(qos: .background).async { or use Task.detached.

但最重要的部分是回到main线程或更特定于 Actor 的线程。

DispatchQueue.main.async是返回主线程的“旧”方式,不应该与async await。苹果提供CheckedContinuation and UncheckedContinuation以此目的。

认识异步/等待 https://developer.apple.com/wwdc21/10132可以再详细说明一下。

import SwiftUI

struct ConcurrentSampleView: View {
    //Solution
    @StateObject var vm: AsyncNumberManager = .init()
    //Just to create a project that can show both scenarios.
    //@StateObject var vm: NumberManager = .init()

    @State var isLoading: Bool = false
    var body: some View {
        HStack{
            //Just to visualize the thread being released
            //If you use NumberManager the ProgressView won't appear
            //If you use AsyncNumberManager the ProgressView WILL appear
            if isLoading{
                ProgressView()
            }
            
            Text(vm.numbers == nil ? "nil" : "\(vm.numbers?.count.description ?? "")")
        }
        //.task is better for iOS 15+
        .onAppear() {
            Task{
                isLoading = true
                await vm.generateNumbers()
                isLoading = false
            }
        }
        
    }
}

struct ConcurrentSampleView_Previews: PreviewProvider {
    static var previews: some View {
        ConcurrentSampleView()
    }
}

@MainActor
class AsyncNumberManager: ObservableObject {
    @Published var numbers: [Double]?
    
    func generateNumbers() async  {
        numbers = await concurrentGenerateNumbers()
    }
    
    private func concurrentGenerateNumbers() async -> [Double]  {
        typealias Cont = CheckedContinuation<[Double], Never>
        return await withCheckedContinuation { (cont: Cont) in
            // This is the asynchronous part, have the operation leave the current actor's thread.
            //Change the priority as needed
            //https://developer.apple.com/documentation/swift/taskpriority
            Task.detached(priority: .utility){
                var numbers = [Double]()
                numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
                //This tells the function to return to the actor's thread
                cont.resume(returning: numbers)
            }
        }
    }
    //Or something like this it just depends on the true scenario
    private func concurrentGenerateNumbers2() async -> [Double]  {
        // This is the asynchronous part, have the operation leave the actor's thread
        //Change the priority as needed
            //https://developer.apple.com/documentation/swift/taskpriority
        return await Task.detached(priority: .utility){
            var numbers = [Double]()
            numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
            return numbers
        }.value
        
    }
    
}
//Incorrect way of applying async/await. This doesn't actually leave the thread or mark when to return. Left here to highlight both scenarios in a reproducible example.
@MainActor
class NumberManager: ObservableObject {
    @Published var numbers: [Double]?
    
    func generateNumbers() async  {
        
        var numbers = [Double]()
        numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
        self.numbers = numbers
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在后台线程中运行异步函数时遇到问题(并发) 的相关文章

  • SwiftUI 导航:如何将详细信息视图切换到不同的项目?

    我正在努力实现以下导航行为 用户可以从列表中选择一个项目 该项目会触发该项目的详细信息视图 在此详细视图上 导航栏中有一个 添加 按钮 可打开用于添加其他项目的模式表 到目前为止 一切都按预期进行 但添加项目后 我希望详细视图显示新项目 我
  • iOS通过AVAssetWriter反转音频

    我正在尝试使用 AVAsset 和 AVAssetWriter 在 iOS 中反转音频 以下代码可以正常工作 但输出文件比输入文件短 例如 输入文件的持续时间为 1 59 但输出文件的持续时间为 1 50 且音频内容相同 void reve
  • Swiftui 应用程序在 iOS 14 / 15 上使用不同的 SearchBar (ViewModifier) 时崩溃

    我在 iOS 14 上使用 SwiftlySearch 在 iOS 15 上使用 searchable struct CompatibleSearchBarModifier ViewModifier Binding var text Str
  • 如何在 Swift 中退出“DispatchQueue.main.asyncAfter”

    我想在调用 deinit 时退出 DispatchQueue main asyncAfter 子视图 swift DispatchQueue main asyncAfter deadline now 5 0 self doSomething
  • 重新定位时 Mapbox 默认的 compassView 会给出奇怪的结果

    我遇到一种情况 必须将地图框罗盘视图重新定位到不同的位置 当我以其他点作为其 compassView 轴旋转地图时 compassView 现在正在旋转 并给了我一个奇怪的结果 附截图 黑色mapBox默认罗盘图标是旋转的 参考截图 这是
  • 新的 iTunes 连接界面错误“您必须选择一个版本”

    当尝试将我的新应用程序提交到应用程序商店时 我注意到苹果已经更改了其网站界面 更新元数据和关键字并点击 提交审核 后 我收到错误 您必须选择一个版本 通过谷歌搜索 我没有找到答案 但是 我注意到 构建 标题旁边有一个加号按钮 您必须在其中选
  • 找不到 -lDoubleConversion 的库

    我尝试在 XCode 上构建 但是ld library not found for lDoubleConversion发生错误 我可以建造react native run ios 这可行 但 XCode 无法构建 ld 警告 找不到选项 L
  • 排除 Realm 模型类

    我的应用程序中配置了两个领域文件 我想存储我的Log将模型与其他模型分开保存为单独的文件 我的问题是我也看到了我的Log我不想要的默认 Realm 文件中的模型类 如何从给定的 Realm 文件中排除特定的模型类 我使用主 Realm 文件
  • Swift 3/4 dash 转驼峰式 (Snake 转驼峰式)

    我正在尝试执行一个简单的破折号到驼峰案例 在 swift 3 或 4 中 this is my id 将变成 thisIsMyId 无论我做什么 我都找不到足够优雅的方法来做到这一点 以下不起作用 str split separator e
  • Modal UIViewController 在 iPad 上总是全屏显示。为什么?

    我试图在 iPad 上创建一个简单的模式对话框 无论是小设置 UIModalPresentationFormSheet 还是大设置 UIModalPresentationPageSheet 但无论我做什么 它们都会全屏显示 带有标题栏 模态
  • Angular/ HTML5 到 iOS WKWebView 通信

    我们正在尝试看看发送信号的最佳选择是什么To iOS WKWebView fromAngular 6 HTML5 项目 任何示例都会很棒 我们想要做的是 我们在 Angular 网页上有一个按钮 单击该按钮应该通知 iOS 执行某些操作 我
  • iOS 上的三字母国家代码

    我知道您可以在 iOS 上获取所有国家 地区的两个字母的国家 地区代码 但是有没有办法获得三个字母的国家代码 So from http en wikipedia org wiki ISO 3166 1 alpha 2 http en wik
  • iOS 9.3 出现新的 UIPDFPageRenderOperation 错误?

    我正在向 UIWebView 添加一些 PDF 链接 每次加载并做出滚动手势时 都会收到此错误 objc 910 UIPDFPageRenderOperation 对象 0x14acaca10 过度释放 当已经解除分配时 打断 objc o
  • Xcode 和 Waze 集成

    我正在尝试整合我的app with waze http www waze com 有人知道如何调用位智并发送坐标吗 我没有找到任何 API 或其他相关信息 void navigateToLatitude double latitude lo
  • 以编程方式进行排序时检索 ViewController 堆栈

    static func showMenuView parentVC UIViewController let storyboard UIStoryboard name Main bundle nil let resultController
  • NSLocale 货币符号,显示金额值之前或之后

    我在用StoreKit在我的应用程序中实现应用程序内购买商店 我有一个自定义设计 这意味着价格的值应该是白色的且较大的 货币符号较小 较暗并与价格值的顶部对齐 我可以使用以下命令毫无问题地获取货币符号NSLocale in SKproduc
  • geocoder.geocodeAddressString 今天不再适用于快速更新

    https developer apple com library prerelease mac releasenotes General APIDiffsMacOSX10 11 Swift CoreLocation html https
  • 奇怪的 Facebook ID [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我有两个 Facebook 应用程序 它们都正在开发中 其中一个是很久以前创建的 而另一个则是相当新的 自从我升级到新的 iOS Faceb
  • 无法在 Swift 中获取 plist URL

    我对这个真的很困惑 网络上有很多问题询问 如何从 Swift 中的 plist 文件获取信息 并且到处都发布了相同的答案 let path NSBundle mainBundle pathForResource Config ofType
  • Expo eas-cli iOS 构建失败

    我已经使用 React Native 创建了一个应用程序 并尝试通过 Expo 的 eas cli 创建一个 iOS 应用程序商店 跑步时eas build platform ios the Fastlane build failed wi

随机推荐