Swift потокобезопасный счетчик переменной? используется для отслеживания, когда удалить индикатор сетевой активности
Краткое объяснение моего сценария:
У меня есть приложение для iOS, которое может выполнять множество сетевых вызовов одновременно, используя разные потоки. В моем iOS-приложении я хочу отобразить встроенный (в строке состояния) индикатор сетевой активности, когда происходит какая-либо сетевая активность.
Итак, я выполнил запуск индикатора активности перед любым сетевым вызовом, а затем остановил его после того, как сетевой вызов либо ошибся, либо завершится успешно.
То, что происходило (очевидно), было то, что, как только одно действие сетевой активности успешно (или с ошибкой) завершилось, тогда он удалял индикатор сетевой активности, даже если я знал, что имела место другая сетевая активность.
Поэтому я решил создать статическую переменную (поскольку многие классы выполняют сетевую активность), которая отслеживает, сколько сетевых запросов выполняется в данный момент (увеличивает эту статическую переменную каждый раз, когда она запускается, а затем уменьшает эту статическую переменную, когда они завершаются (либо успешно или не удалось), только когда этот счетчик достигнет 0, он отключит индикатор сетевой активности.
Это работает нормально, однако теперь мы переходим к моему вопросу - множество различных потоков обновляют эту статическую переменную, поэтому я думаю, что она должна быть поточно-ориентированной?
Каков наилучший способ сделать это - должен ли я помещать каждый вызов для запуска / остановки индикатора активности (который, в свою очередь, увеличивает / уменьшает статическую переменную) в главном потоке, который затем гарантирует, что эта переменная будет также считываться / записываться в последовательном режиме?
Или я читал об использовании семафоров, чтобы сказать потоку ждать, если переменная заблокирована, и затем запускать, когда другой поток заканчивает с ней как для чтения, так и для записи переменной?
Извините за длинный вопрос, но я хотел дать вам сценарий моей нынешней ситуации.
заранее спасибо
2 ответа
Самый простой способ добиться этого - использовать последовательную очередь. Вы можете использовать основную очередь DispatchQueue.main.async { /* increment or decrement counter */ }
или же DispatchQueue.main.sync { /* increment or decrement counter */ }
Или вы можете создать новую последовательную очередь: let counterQueue = DispatchQueue(label: "counter-queue")
и использовать его для той же цели.
Как сказал Святослав Якымив, очередь последовательной отправки может быть использована для синхронизации доступа к счетчику, который хранит текущее количество невыполненных сетевых запросов.
Можно использовать DispatchQueue.main
для этой цели, потому что UIApplication.shared.isNetworkActivityIndicatorVisible
все равно должен быть доступен только в главной очереди.
Кроме того, управление счетчиком и индикатором сети может быть инкапсулировано в класс в стиле RAII:
final public class NetworkActivity {
private static var count = 0
public init() {
DispatchQueue.main.async {
if NetworkActivity.count == 0 {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
NetworkActivity.count += 1
}
}
deinit {
DispatchQueue.main.async {
NetworkActivity.count -= 1
if NetworkActivity.count == 0 {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}
}
Создание NetworkActivity
instance увеличивает глобальный счетчик, а первый экземпляр делает индикатор сети видимым. Уничтожение объекта уменьшает счетчик, а индикатор скрывается при уничтожении последнего экземпляра.
Пример использования:
let request = URLRequest(...)
var activity: NetworkActivity? = NetworkActivity()
_ = activity // To suppress a "variable was written to, but never read" warning
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
activity = nil
// ...
}
task.resume()