Назад к вопросам
Middle+
136
questionbank
Как решить проблему гонки данных (race condition)?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Использовать синхронизацию потоков доступа к общим ресурсам.
Основные подходы:
- Mutexes (Мьютексы): Блокируют доступ к ресурсу для других потоков, пока один поток его использует.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Захватываем мьютекс
// Критическая секция - безопасный доступ к sharedResource
sharedResource += 1
print("Resource value: \(sharedResource)")
lock.unlock() // Освобождаем мьютекс
}
// Пример вызова из разных потоков
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Serial Queues (Последовательные очереди): Исполняют задачи одну за другой, гарантируя, что одновременно только один блок кода имеет доступ к ресурсу.
DispatchQueue.mainDispatchQueue.global()с атрибутом.serial
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Код выполняется последовательно в рамках этой очереди
sharedResource += 1
print("Resource value: \(sharedResource)")
}
}
// Пример вызова из разных потоков/очередей
// modifyResourceAsync()
// modifyResourceAsync()
- Reader-Writer Locks (Блокировка читатель-писатель): Позволяют множеству потоков читать ресурс одновременно, но только одному потоку писать в него.
DispatchQueueс барьерами (.barrier) для записи и синхронным/асинхронным доступом для чтения.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Чтение - множество читателей одновременно
print("Reading resource: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Запись - только один писатель и никто не читает/пишет
sharedResource = newValue
print("Writing resource: \(sharedResource)")
}
}
// Пример вызовов
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Atomic Operations (Атомарные операции): Операции, которые гарантированно выполняются целиком без прерывания другим потоком. Используются для простых типов данных (счетчики, флаги).
OSAtomicIncrement,OSAtomicDecrement(Устаревшие, но концепция актуальна)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Атомарное инкрементирование
std::cout << "Counter: " << atomicCounter << std::endl;
}
// Пример C++ в контексте iOS (использование с Objective-C++)
// Вызывать из разных потоков
- Value Types (Типы значений): Передача копий структур/перечислений вместо ссылок на классы может помочь избежать проблем с гонкой данных, если данные не разделяются напрямую между потоками в изменяемом виде.
Выбор метода зависит от специфики задачи и требуемого уровня параллелизма. Для большинства задач в iOS предпочтительны DispatchQueue (Serial и Concurrent с барьерами) из-за легкости использования и интеграции с Grand Central Dispatch (GCD).