Ответ
Потокобезопасность гарантирует корректность данных при одновременном доступе из нескольких потоков. Вот основные подходы в Swift:
1. Использование DispatchQueue с барьером (barrier)
Наиболее распространённый и гибкий способ для чтения/записи.
class ThreadSafeContainer<T> {
private var value: T
private let queue = DispatchQueue(label: "com.example.threadsafe", attributes: .concurrent)
init(value: T) { self.value = value }
var currentValue: T {
get {
return queue.sync { value } // Конкурентное чтение
}
set {
queue.async(flags: .barrier) { // Эксклюзивная запись
self.value = newValue
}
}
}
}
2. Использование мьютексов (Mutex) Более низкоуровневый, но эффективный контроль.
import os
class ThreadSafeValue<T> {
private var value: T
private let lock = os_unfair_lock()
init(value: T) { self.value = value }
func update(_ block: (inout T) -> Void) {
os_unfair_lock_lock(&lock)
defer { os_unfair_lock_unlock(&lock) }
block(&value)
}
func read() -> T {
os_unfair_lock_lock(&lock)
defer { os_unfair_lock_unlock(&lock) }
return value
}
}
3. Property Wrapper (обёртка свойства) Удобный современный способ инкапсулировать логику синхронизации.
@propertyWrapper
struct ThreadSafe<T> {
private var value: T
private let lock = NSLock()
init(wrappedValue: T) {
self.value = wrappedValue
}
var wrappedValue: T {
get {
lock.lock()
defer { lock.unlock() }
return value
}
set {
lock.lock()
defer { lock.unlock() }
value = newValue
}
}
}
// Использование
class Model {
@ThreadSafe var counter: Int = 0
}
Важно: Потокобезопасность переменной не гарантирует потокобезопасность всей системы. Необходимо анализировать все точки доступа к общим данным.
Ответ 18+ 🔞
А, ну это же классика, блядь! Потокобезопасность, сука! Такая тема, от которой у новичков мозг вскипает, как каша в детсаду, а опытные только вздыхают и думают: "опять эту песню петь, нахуй".
Смотри, в чём суть-то, ёпта. Представь себе, у тебя есть одна бутылка водки на столе. И к ней одновременно тянутся руки из пяти разных потоков. Что будет? Правильно, драка, блядь, мордобой и все останутся без водки, а бутылка разобьётся. Вот чтобы такого пиздеца не было, и нужны эти все штуки — мьютексы, очереди и прочая хуйня.
1. Очередь с барьером — наш бро, проверенный временем Это как завести в свою квартиру строгого, но справедливого швейцара. Читать могут все, кто пришёл, а вот чтобы что-то записать или изменить — нужно ждать, пока все читатели уйдут, и только тогда тебе дадут зелёный свет. В коде это выглядит так, блядь:
class ThreadSafeContainer<T> {
private var value: T
private let queue = DispatchQueue(label: "com.example.threadsafe", attributes: .concurrent)
init(value: T) { self.value = value }
var currentValue: T {
get {
return queue.sync { value } // Читаем все вместе, как в библиотеке
}
set {
queue.async(flags: .barrier) { // А пишем по одному, как в сортире
self.value = newValue
}
}
}
}
Главное, блядь, не перепутай — .sync для чтения, .async(.barrier) для записи. А то будет не потокобезопасность, а потокопиздец.
2. Мьютекс — старый, злой, но быстрый дед Это уже по-серьёзному, на низком уровне. Как будто ты не швейцара нанял, а поставил у бутылки здорового качка с битой, который говорит: "Подходи по одному, пидоры". Быстрее, но и ответственности больше, ебать.
import os
class ThreadSafeValue<T> {
private var value: T
private let lock = os_unfair_lock()
init(value: T) { self.value = value }
func update(_ block: (inout T) -> Void) {
os_unfair_lock_lock(&lock)
defer { os_unfair_lock_unlock(&lock) }
block(&value)
}
func read() -> T {
os_unfair_lock_lock(&lock)
defer { os_unfair_lock_unlock(&lock) }
return value
}
}
defer — это наше всё, ёпта. Заблокировал — обязательно разблокируй, даже если посередине вылетит исключение. А то все потоки зависнут и будут ждать, пока солнце взойдёт на западе.
3. Property Wrapper — модный, молодёжный способ Swift, сука, не стоит на месте. Теперь можно сделать такую красивую обёрточку, что самому приятно смотреть. Выглядит как конфетка, а внутри всё тот же здоровый качок с битой.
@propertyWrapper
struct ThreadSafe<T> {
private var value: T
private let lock = NSLock()
init(wrappedValue: T) {
self.value = wrappedValue
}
var wrappedValue: T {
get {
lock.lock()
defer { lock.unlock() }
return value
}
set {
lock.lock()
defer { lock.unlock() }
value = newValue
}
}
}
// Использование — просто пиздец как элегантно
class Model {
@ThreadSafe var counter: Int = 0
}
Объявил свойство с @ThreadSafe и вроде как спать спокойно можешь. Но нет, блядь!
И вот тут, сука, главный подвох! Запомни раз и навсегда, как "Отче наш": потокобезопасность ОДНОЙ ПЕРЕМЕННОЙ — это НЕ потокобезопасность ВСЕЙ СИСТЕМЫ, блядь!
Это как поставить бронированную дверь в халупе, но оставить окно нараспашку. Можно сколько угодно синхронизировать доступ к counter, но если у тебя два потока сначала читают его значение, потом каждый что-то своё вычисляет, а потом пишут обратно — будет classic race condition, ёбаный в рот! Потому что между чтением и записью значение могло уже десять раз поменяться другим потоком.
Так что, чувак, не расслабляйся. Синхронизировал одну штуку — иди проверяй, нет ли ещё общих данных, к которым лезут со всех щелей. Анализируй ВСЕ точки доступа, а не одну. Иначе получится история, как с тем Герасимом: вроде всё правильно сделал, а в итоге — Муму, блядь, на дне.