Ответ
Да, но доступ должен быть синхронизирован. Каждый RunLoop привязан к своему потоку. Прямой доступ к общему ресурсу из нескольких потоков (и, следовательно, RunLoop) без синхронизации приводит к состоянию гонки (race condition) и неопределенному поведению.
Способы синхронизации:
- Serial DispatchQueue: Гарантирует последовательный доступ.
- Concurrent DispatchQueue с барьером: Для эффективного чтения/записи.
- Мьютексы (NSLock, os_unfair_lock): Для низкоуровневой синхронизации.
Пример с использованием DispatchQueue:
// Потокобезопасный контейнер
class SharedResource<T> {
private var value: T
private let queue = DispatchQueue(label: "com.example.shared", attributes: .concurrent)
init(_ value: T) { self.value = value }
func read() -> T {
return queue.sync { value }
}
func write(_ newValue: T) {
queue.async(flags: .barrier) { self.value = newValue }
}
}
Вывод: Сам RunLoop не обеспечивает синхронизацию. Используйте стандартные механизмы многопоточности для защиты общих данных.
Ответ 18+ 🔞
А, ну тут, блядь, классика жанра! Слушай, как есть. Вот ты говоришь — «да, но доступ должен быть синхронизирован». Ну да, а как же иначе-то, ёпта? А то сейчас начнётся: один поток пишет, другой читает, третий вообще мимо проходил и случайно ногой задел — и всё, пиздец, приехали. Race condition, неопределённое поведение, краш, слезы, сопли, и вся хуйня.
Каждый этот твой RunLoop, он же как собака на цепи — привязан к своему потоку, к своей конуре. И если с одной цепи спустить, а с другой тоже, да ещё и на одну кость — они друг другу морды порвут, блядь. Прямой доступ к общему ресурсу из нескольких потоков (а значит, и из нескольких RunLoop) без синхронизации — это как зайти в чужую квартиру и начать там мебель переставлять, пока хозяин спит. Хуй знает, чем кончится.
Ну и как с этим бороться, спросишь ты? А вот как, слушай сюда, вариантов — овердохуища:
- Serial DispatchQueue. Это как очередь в одну кассу в совковом магазине. Все стоят, ждут, никто не лезет. Гарантирует, что доступ будет строго по одному, последовательно. Просто, надёжно, иногда медленно, если народу много.
- Concurrent DispatchQueue с барьером. Это уже похитрее. Читателей — хоть толпу запускай, они все одновременно могут смотреть. Но как только пришёл писатель (барьерная задача) — все, стоп, блядь! Все читатели ждут, пока этот один чувак всё запишет, и только потом снова пускают толпу. Эффективно, умно.
- Мьютексы всякие (NSLock, os_unfair_lock). Ну это уже низкоуровневая ручная работа, как с гаечным ключом. Захватил замок — работай, отпустил — другой может взять. Мощно, но можно и палец прищемить, если неаккуратно.
Вот, смотри, пример на DispatchQueue, чтоб понятнее было:
// Потокобезопасный контейнер (чтобы не было как в том анекдоте про Герасима)
class SharedResource<T> {
private var value: T // Само ценное, за чем все гоняются
private let queue = DispatchQueue(label: "com.example.shared", attributes: .concurrent) // Наша хитрая очередь
init(_ value: T) { self.value = value }
// Почитать может кто угодно, одновременно
func read() -> T {
return queue.sync { value } // sync — чтобы сразу результат вернул
}
// А чтобы записать — все, стопэ, блядь, барьер!
func write(_ newValue: T) {
queue.async(flags: .barrier) { self.value = newValue } // async с барьером — пиши, пока все ждут
}
}
И главный вывод, который в башку надо вбить: Сам по себе RunLoop — него синхронизацию не обеспечивает, ни хуя. Он просто цикл в потоке. Всё, что между потоками — это твоя головная боль. Так что не надейся на авось, используй нормальные, человеческие механизмы многопоточности, чтобы общие данные не превратились в помойку. А то будет потом: «ой, а у меня тут значение иногда не то» — да потому что ты, блядь, его в состоянии гонки десять раз перезаписал, вот оно и не то!