使用调度组时如何使公共资源线程安全?

How to make a common resource thread safe when using dispatch group?

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

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。但我不确定这是否是正确的方法。

您问的是:

But I am not clear about the thread-safety of my user object. Since it will be updated in three different callbacks, would thread safety be an issue here?

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

Closures passed to response handlers are executed on the .main queue by default, but a specific DispatchQueue can be passed on which to execute the closure.

所以除非你做了一些不寻常的事情(比如用一些并发的 DispatchQueue 覆盖默认的 .main 队列),Alamofire 将 运行 它在主线程上的完成处理程序,减轻线程安全问题。

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

最重要的是,只要您不是同时来自多个线程的 mutating/accessing 变量,线程安全问题就会在很大程度上得到缓解。