Middle+
Чӣ гуна масъалаи рақобат (race condition) бояд ҳал карда шавад?
sobes.tech AI
Ҷавоб аз AI
Використання синхронізації потоків для доступу до спільних ресурсів.
Основні підходи:
- Mutexes (Мютекси): Блокують доступ до ресурсу для інших потоків, поки один потік його використовує.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Захоплюємо мютекс
// Критична секція - безпечний доступ до sharedResource
sharedResource += 1
print("Значення ресурсу: \(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("Значення ресурсу: \(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("Читаємо ресурс: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Запис - лише один писач і ніхто не читає/пише
sharedResource = newValue
print("Пишемо ресурс: \(sharedResource)")
}
}
// Приклад викликів
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Атомарні операції: Операції, які гарантовано виконуються цілком без переривання іншим потоком. Використовуються для простих типів даних (лічильники, прапорці).
OSAtomicIncrement,OSAtomicDecrement(застарілі, але концепція актуальна)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Атомарне інкрементування
std::cout << "Лічильник: " << atomicCounter << std::endl;
}
// Приклад C++ у контексті iOS (використання з Objective-C++)
// Викликати з різних потоків
- Типи значень (Value Types): Передача копій структур або перерахувань може допомогти уникнути проблем з гонкою даних, якщо дані не діляться безпосередньо між потоками у змінюваному вигляді.
Вибір методу залежить від специфіки задачі та потрібного рівня паралелізму. Для більшості задач у iOS переважно використовувати DispatchQueue (послідовні та паралельні з бар'єрами) через легкість використання та інтеграцію з Grand Central Dispatch (GCD).