首页 > 解决方案 > 使用调度组时如何使公共资源线程安全?

问题描述

我有一个用户类,每次用户打开应用程序时都需要更新

class User : NSObject, NSCoding {
    var vehicles : [Vehicles]
    var bankaccounts : [BankAccounts]
    var friends : [Friends]
}

在我的主屏幕 ViewController 中,我有一个使用 3 个 Alamofire 请求从后端获取数据的函数。最后,我将数据保存在 UserDefaults 中。DispatchGroup 是我首先想到的实现这一点。这是代码

func loadUserData {
    var user = User()

    let userDataDispatchGroup = DispatchGroup()

    userDataDispatchGroup.enter()
    AF.request(...).responseJSON {
        //update the user.vehicles array
        userDataDispatchGroup.leave()
    }
    
    userDataDispatchGroup.enter()
    AF.request(...).responseJSON {
        //update the user.bankaccounts array
        userDataDispatchGroup.leave()
    }

    userDataDispatchGroup.enter()
    AF.request(...).responseJSON {
        //update the user.friends array
        userDataDispatchGroup.leave()
    }

    userDataDispatchGroup.notify(queue: .main) {
        let encodedData  = NSKeyedArchiver.archivedData(withRootObject: user)
        UserDefaults.standard.set(encodedData, forKey: "user")
    }

}

但我不清楚我的用户对象的线程安全性。由于它将在三个不同的回调中更新,线程安全在这里会是一个问题吗?如果是,解决问题的最佳方法是什么?我正在考虑使用 DispatchSemaphore。但我不确定这是否是正确的方法。

标签: iosswiftgrand-central-dispatchdispatchgroupdispatchsemaphore

解决方案


您问:

但我不清楚我的用户对象的线程安全性。由于它将在三个不同的回调中更新,线程安全在这里会是一个问题吗?

您的代码片段中没有线程安全问题,因为 Alamofire 在主线程上调用其完成处理程序。他们这样做是为了帮助减轻多线程问题。DispatchQueue.main.async在这种情况下不需要任何东西。正如 Alamofire文档所说

默认情况下,传递给响应处理程序的闭包在队列上执行,但可以传递.main特定的闭包来执行闭包。DispatchQueue

因此,除非您做了一些不寻常的事情(例如.main用一些并发覆盖默认队列DispatchQueue),否则 Alamofire 将在主线程上运行其完成处理程序,从而减轻线程安全问题。

如果您使用的是不同的 API,它没有在主线程上URLSession.shared调用其完成处理程序(例如,在后台队列上调用其完成处理程序),那么可能会有问题,但 Alamofire 不会。(甚至URLSession使用串行后台队列,因此使用您的模式不会有问题,您正在更新局部变量。)

最重要的是,只要您不同时从多个线程更改/访问变量,线程安全问题就会大大减轻。


推荐阅读