Рабочая блокировка NSCondition
В последнее время я использую NSCondition, и я действительно не понимаю часть блокировок и разблокировок.
Например,
NSCondition lock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do stuff
[lock signal];
};
[lock wait];
//Do rest
Это работает нормально. Какая разница от этого..
NSCondition lock = [[NSCondition alloc] init];
[lock lock];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do stuff
[lock signal];
[lock unlock];
};
[lock wait];
//Do rest
1 ответ
Вы должны удерживать блокировку переменной условия перед ожиданием. Ожидание снимает блокировку, затем гарантированно блокируется до тех пор, пока она не будет сигнализирована и блокировка не будет снята повторно. Обратите внимание, что если несколько потоков ожидают, только один будет уведомлен о сигнале, другие будут продолжать ждать дальнейших сигналов. В качестве альтернативы, если предикат останется истинным после выполнения, тогда сигнал может быть передан всем потокам.
Блокировка - это взаимная исключающая блокировка (мьютекс), то есть только один поток может удерживать блокировку одновременно. Помимо использования этого для защиты того, для чего вы используете переменную условия, оно также защищает внутреннюю работу переменной условия. Если вы попытаетесь использовать его без блокировки, то при проверке / установке условия в нескольких потоках возникнут условия гонки.
Для правильного использования условной переменной ни один из ваших примеров не является правильным. Все дело в том, чтобы защищать состояние предиката. Вот пример, основанный на псевдокоде из документации.
NSCondition condvar = [NSCondition new];
__block BOOL workRequired = NO;
// something somewhere else does this at some point ^{
[condvar lock];
workRequired = YES;
[condvar signal];
[condvar unlock];
};
[condvar lock];
while (!workRequired) {
[condvar wait];
}
// Do the work
workRequired = NO;
[condvar unlock];
В вашем примере у вас нет никаких условий, кроме "блок закончен". В этом случае вы должны просто использовать dispatch_sync.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do stuff
};
// Do rest
Если по какой-то причине вы не знаете, работаете ли вы в этой очереди, то семафора будет достаточно.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0L);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do stuff
dispatch_semaphore_signal(semaphore);
};
dispatch_semaphore_wait(semaphore);
// Do rest