Ответ
В Swift с помощью Grand Central Dispatch (GCD) атомарность достигается через серийные очереди или барьеры в конкурентных очередях. Оба подхода гарантируют, что критическая секция выполняется без гонки данных.
1. Использование серийной очереди (проще, но менее гибко)
private let serialQueue = DispatchQueue(label: "com.example.atomic.serial")
private var sharedResource: Int = 0
func updateResource(_ newValue: Int) {
serialQueue.async {
self.sharedResource = newValue
}
}
func readResource() -> Int {
return serialQueue.sync {
return self.sharedResource
}
}
2. Использование барьеров в конкурентной очереди (высокая производительность при чтении)
private let concurrentQueue = DispatchQueue(label: "com.example.atomic.concurrent", attributes: .concurrent)
private var sharedResource: Int = 0
func updateResource(_ newValue: Int) {
// Барьер гарантирует эксклюзивный доступ на запись
concurrentQueue.async(flags: .barrier) {
self.sharedResource = newValue
}
}
func readResource() -> Int {
// Множественные чтения могут выполняться параллельно
return concurrentQueue.sync {
return self.sharedResource
}
}
Ключевые моменты:
- Барьеры (
flags: .barrier) блокируют очередь на время выполнения задачи, обеспечивая атомарность записи. - Серийная очередь выполняет задачи строго последовательно, что также обеспечивает атомарность.
- Для сложных структур данных или Swift 5.5+ рассмотрите использование акторов (actors) как более современной альтернативы.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши атомарные танцы с бубном в Swift! Слушай, тут всё просто, как три копейки, но если накосячить — получишь гонку данных, и потом будешь искать багу неделями, блядь. Так что давай разберём, как не обосраться с многопоточностью.
Вот смотри, есть два классических подхода через GCD, оба работают, но с разным приколом.
Первый способ — серийная очередь. Тупо, но надёжно, как советский замок.
Представь, что у тебя есть общая переменная, и к ней лезут сразу с нескольких потоков. Это пиздец, чувак. Чтобы не было драки, мы создаём очередь, которая работает как строгий охранник: задачи выполняет строго по одной, друг за другом. Никакого параллелизма, всё чинно-благородно.
private let serialQueue = DispatchQueue(label: "com.example.atomic.serial")
private var sharedResource: Int = 0
func updateResource(_ newValue: Int) {
serialQueue.async {
self.sharedResource = newValue
}
}
func readResource() -> Int {
return serialQueue.sync {
return self.sharedResource
}
}
Суть в чём? serialQueue.async — это значит «сделай это когда-нибудь, но обязательно по порядку». А serialQueue.sync — это «стой, мудак, ни шагу дальше, пока я тут значение не прочитаю и не верну тебе его». Всё атомарно, гонок нет. Но есть минус: если у тебя чтений дохуя, они все будут вставать в эту же очередь и ждать своей очереди, даже друг друга не пропуская. Не очень-то эффективно.
Второй способ — барьеры в конкурентной очереди. Это уже для тех, кто хочет выжать максимум.
Тут идея хитрая, как жопа верблюда. Мы даём зелёный свет на чтение из разных потоков одновременно — это же безопасно! Но когда нужно записать — ставим шлагбаум, ёпта! Барьер. Пока запись идёт, все чтения и другие записи стоят и ждут.
private let concurrentQueue = DispatchQueue(label: "com.example.atomic.concurrent", attributes: .concurrent)
private var sharedResource: Int = 0
func updateResource(_ newValue: Int) {
// Барьер гарантирует эксклюзивный доступ на запись
concurrentQueue.async(flags: .barrier) {
self.sharedResource = newValue
}
}
func readResource() -> Int {
// Множественные чтения могут выполняться параллельно
return concurrentQueue.sync {
return self.sharedResource
}
}
Видишь магию? Обычные чтения (concurrentQueue.sync) летят параллельно, не мешая друг другу. А вот запись (flags: .barrier) — это такой жирный кирпич, который перекрывает всю очередь на время своего выполнения. После него снова запускается весь этот цирк с конями. Производительность чтений — зашибись!
Итог, блядь:
- Серийная очередь (
serialQueue) — для ленивых или когда операции простые и быстрые. Всё последовательно, мозг не надо напрягать. - Барьеры (
flags: .barrier) — когда чтений много, а записей мало. Чтения летают свободно, а запись встаёт как царь горы и всех блокирует. - И да, если ты уже на Swift 5.5+, то можешь вообще посмотреть в сторону акторов (actors). Это новая, модная, молодёжная хуйня, которая многое делает за тебя. Но старые добрые очереди — они как папин Запорожец: криво, косо, но едет и ломаться нехуя!