Я знаю, что опаздываю на вечеринку с этим, но я обнаружил странное сочетание обстоятельств, которое @synchronized
плохо работает и, вероятно, несет ответственность за вашу проблему. У меня нет решения для этого, кроме того, чтобы изменить код, чтобы устранить причину, когда вы знаете, что это такое.
Я буду использовать этот код для демонстрации.
- (int)getNumberEight {
@synchronized(_lockObject) {
// Point A
return 8;
}
}
- (void)printEight {
@synchronized(_lockObject) {
// Point B
NSLog(@"%d", [self getNumberEight]);
}
}
- (void)printSomethingElse {
@synchronized(_lockObject) {
// Point C
NSLog(@"Something Else.");
}
}
Как правило, @synchronized
является рекурсивно безопасный замок. Таким образом, вызов [self printEight]
в порядке и не вызывает взаимоблокировок. То, что я нашел, является исключением из этого правила. Следующая серия событий вызовет взаимоблокировку и будет чрезвычайно трудно отследить.
- Резьба 1 вводит
-printEight
и приобретает замок.
- Резьба 2 вводит
-printSomethingElse
и пытается приобрести замок. Блокировка удерживается нитью 1, поэтому она находится в очереди, чтобы ждать, пока блокировка не будет доступна и блокируется.
- Резьба 1 введите
-getNumberEight
и попытается приобрести замок. Замок уже удерживается, а кто-то еще находится в очереди, чтобы получить его дальше, так что блоки потока 1. Тупик.
Похоже, что эта функциональность является непреднамеренным следствием желания связать голодание при использовании @synchronized
. Блокировка только рекурсивно безопасна, когда ни одна другая нить не ждет ее.
В следующий раз, когда вы попадаете в тупик в своем коде, изучите стеки вызовов в каждом потоке, чтобы узнать, содержит ли какой-либо из заблокированных потоков уже блокировку. В приведенном выше примере кода, добавляя длинные спящие точки в точках A, B и C, тупик можно воссоздать с почти 100% консистенцией.
EDIT:
Я больше не в состоянии продемонстрировать предыдущий вопрос, но есть родственная ситуация, которая до сих пор вызывает вопросы. Это связано с динамическим поведением dispatch_sync
.
В этом коде есть две попытки рекурсивно получить блокировку. Первые вызовы из основной очереди в фоновую очередь. Второй вызов из фоновой очереди в основную очередь.
В чем разница в поведении - это различие между очередями отправки и потоками.Первый пример вызывает другую очередь, но никогда не меняет потоки, поэтому рекурсивный мьютекс приобретается. Второе изменение потоков при изменении очередей, поэтому рекурсивный мьютекс не может быть получен.
Чтобы подчеркнуть, эта функциональность по дизайну, но это может быть неожиданно для некоторых, которые не понимают GCD, а также могли.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSObject *lock = [[NSObject alloc] init];
NSTimeInterval delay = 5;
NSLog(@"Example 1:");
dispatch_async(queue, ^{
NSLog(@"Starting %d seconds of runloop for example 1.", (int)delay);
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]];
NSLog(@"Finished executing runloop for example 1.");
});
NSLog(@"Acquiring initial Lock.");
@synchronized(lock) {
NSLog(@"Acquiring recursive Lock.");
dispatch_sync(queue, ^{
NSLog(@"Deadlock?");
@synchronized(lock) {
NSLog(@"No Deadlock!");
}
});
}
NSLog(@"\n\nSleeping to clean up.\n\n");
sleep(delay);
NSLog(@"Example 2:");
dispatch_async(queue, ^{
NSLog(@"Acquiring initial Lock.");
@synchronized(lock) {
NSLog(@"Acquiring recursive Lock.");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Deadlock?");
@synchronized(lock) {
NSLog(@"Deadlock!");
}
});
}
});
NSLog(@"Starting %d seconds of runloop for example 2.", (int)delay);
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]];
NSLog(@"Finished executing runloop for example 2.");
Просто, чтобы сузить проблему - попробуйте заменить ваш @synchronized на [замок блокировки] и [блокировки разблокировки] на общем случае NSLock. – Stavash
Это легче сказать, чем сделать. :-) Эта блокировка происходит примерно раз в неделю с использованием сотен часов при использовании между ними. Также я понимаю, что то, о чем вы говорите, именно то, что @synchronized делает внутренне. –
Ну, не совсем. Для одного это значительно быстрее. Взгляните на http://perpendiculo.us/?p=133 – Stavash