Как сделать общий ресурс потокобезопасным при использовании диспетчерской группы?

У меня есть класс User, который нужно обновлять каждый раз, когда пользователь открывает приложение.

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. Но я не уверен, что это правильный подход.

1 ответ

Решение

Ты спрашивал:

Но мне непонятна безопасность потоков моего пользовательского объекта. Поскольку он будет обновляться в трех разных обратных вызовах, будет ли здесь проблема с потокобезопасностью?

В вашем фрагменте кода нет проблем с безопасностью потоков, потому что Alamofire вызывает свои обработчики завершения в основном потоке. Они делают это, чтобы уменьшить проблемы с многопоточностью. Нет необходимости вDispatchQueue.main.asyncв этом случае. Как говорится в документации Alamofire:

Замыкания, передаваемые обработчикам ответов, выполняются на .main очередь по умолчанию, но определенная DispatchQueue можно передать, чтобы выполнить закрытие.

Поэтому, если вы не сделали что-то необычное (например, переопределили значение по умолчанию .main очередь с некоторыми параллельными DispatchQueue), Alamofire будет запускать свои обработчики завершения в основном потоке, уменьшая проблемы безопасности потоков.

Если вы использовали другой API, который не вызывал обработчики завершения в основном потоке (например, URLSession.sharedвызывает обработчики завершения в фоновой очереди), тогда могут возникнуть проблемы, но не с Alamofire. (И дажеURLSession использует последовательную фоновую очередь, поэтому не будет проблем с использованием вашего шаблона, когда вы обновляете локальную переменную.)

Итог: пока вы не изменяете / не получаете доступ к переменной из нескольких потоков одновременно, проблемы безопасности потоков в значительной степени смягчаются.

Другие вопросы по тегам