Ответ
Барьерные задачи (Dispatch barrier) в GCD обеспечивают синхронизацию доступа к общему ресурсу в concurrent очередях.
Преимущества:
- Синхронизация записи при параллельном чтении: Гарантирует, что задача записи выполнится только после завершения всех ранее поставленных задач, а новые задачи начнут выполняться только после барьерной.
- Избегание гонок данных: Полезен для операций модификации shared ресурсов (например, массива или словаря).
- Эффективность: Позволяет сохранить параллельное выполнение задач чтения, блокируя только на время записи.
Недостатки и ограничения:
- Только для custom concurrent очередей: Не работает с глобальными очередями (
DispatchQueue.global()) или serial очередями. - Риск взаимоблокировок (deadlock): При неправильном вложенном использовании (например, sync-барьер в той же очереди).
- Избыточность для простых случаев: Для исключительного доступа к ресурсу проще использовать serial очередь.
Пример использования в Swift:
let concurrentQueue = DispatchQueue(label: "com.example.queue", attributes: .concurrent)
var sharedArray = [String]()
// Параллельные операции чтения
for i in 0..<5 {
concurrentQueue.async {
print("Чтение: (sharedArray)")
}
}
// Барьерная операция записи (выполнится одна, после всех чтений)
concurrentQueue.async(flags: .barrier) {
sharedArray.append("New Item")
print("Запись завершена")
}
// Последующие задачи (начнутся только после барьера)
concurrentQueue.async {
print("После барьера: (sharedArray)")
} Ответ 18+ 🔞
А, слушай, вот эта штука с барьерными задачами в GCD — это ж, блядь, как такой хитрый замок на толкучке! Представь: у тебя куча народу параллельно тащит из твоего общего ящика хуйню (читает), а ты такой: «А щас я, сука, приду и своё положу (запишу)». И чтобы никто в этот момент не сунул свои кривые ручонки, ты ставишь здорового верзилу-барьера.
В чём прикол, на самом деле:
- Читай, не хочу, но писать — по одному: Пока этот верзила не выполнит свою работу, все, кто хотел что-то прочитать — читают себе спокойно. А вот новые желающие подождут, пока он закончит своё чёрное дело. Идеально, чтобы не было этой ебаной гонки данных, когда два потока одновременно лезут в один словарь и получается пиздец и каша.
- Эффективность, мать её: Не надо всю очередь превращать в serial и делать всё по очереди, как последние лузеры. Чтение — пожалуйста, параллельно. А вот запись — разок подождали, зато всё чисто.
Но и подводные ебли, куда ж без них:
- Только свои, блядь, очереди: Эту фишку нельзя впендюрить в глобальные системные очереди (
DispatchQueue.global()). Только в свои собственные, которые ты создал с флагом.concurrent. В serial очередях это вообще бессмысленно, там и так всё по одному. - Мёртвая хватка (deadlock): Если ты внутри барьерной задачи, которая выполняется асинхронно (
async), вдруг решишь синхронно (sync) вызвать ещё что-то на ЭТОЙ ЖЕ очереди — всё, пизда, зависнешь навечно. Сам себя ждёшь, дурачок. - Из пушки по воробьям: Если у тебя ресурс настолько хрупкий, что к нему вообще всегда только по одному можно подходить, то проще сразу использовать serial очередь и не париться с барьерами.
Ну и как это выглядит в коде, смотри:
// Создаём свою concurrent очередь, а не системную!
let concurrentQueue = DispatchQueue(label: "com.example.queue", attributes: .concurrent)
var sharedArray = [String]() // Наш общий ресурс, который все хотят
// Параллельные операции чтения — все лезут сразу, как тараканы
for i in 0..<5 {
concurrentQueue.async {
print("Чтение: (sharedArray)")
}
}
// А вот тут — БАРЬЕР, сука! Все встали. Запись будет только ОДНА.
concurrentQueue.async(flags: .barrier) {
sharedArray.append("New Item") // Модифицируем ресурс в гордом одиночестве
print("Запись завершена")
}
// И только после того, как барьерная задача отъебалась,
// эти задачи снова побегут параллельно
concurrentQueue.async {
print("После барьера: (sharedArray)")
}
Короче, штука мощная, но, как и всё в многопоточности, требует, чтобы башка думала. А то так и до взаимоблокировок недалеко, ёпта.