我知道我迟到了,但我发现了一个奇怪的情况组合,@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%一致地重新创建死锁。
编辑:
我不再能证明以前的问题,但有一个相关的情况仍然会造成一些问题。它与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 2012-04-15 15:53:12
说起来容易做起来难。 :-)这种锁定每周大概发生一次,并在几个小时之间使用。这也是我的理解,你提到的内容正是@synchronized在内部做的。 – 2012-04-15 16:10:20
呃,不完全是。对于一个它显着更快。看看http://perpendiculo.us/?p=133 – Stavash 2012-04-15 16:13:38