locking等待@synchronized
我有这个(罕见)奇怪的情况下,我的Objective-C iOS程序被locking。 当我进入debugging器时,有两个线程,两个都停留在@synchronized()。
除非我完全同步误解,否则我不认为这是可能的,也不是指挥的重点。
我有一个主线程和工作线程,都需要访问一个SQLite数据库,所以我包装访问数据库在@synchronized(myDatabase)块的代码块。 除了db访问以外,在这些块中没有其他的事情发生。
我也使用FMDatabase框架来访问sqlite,我不知道这是否重要。
myDatabase是一个包含FMDatabase对象的全局variables。 它在程序开始时创build一次。
我知道我迟到了这个派对,但是我发现@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
是一个recursion安全的锁。 因此,调用[self printEight]
是可以的,不会导致死锁。 我发现的是该规则的例外情况。 以下一系列事件将导致死锁,并且极难追查。
- 线程1进入
-printEight
并获取locking。 - 线程2input
-printSomethingElse
并尝试获取该锁。 锁由线程1保持,因此它被排队等待,直到锁可用并阻塞。 - 线程1input
-getNumberEight
并尝试获取locking。 锁已经被占用,而其他人在队列中接下来,所以线程1阻塞。 僵局。
看起来,这个function是在使用@synchronized
时想要限制饥饿的一个意想不到的结果。 当没有其他线程正在等待时,锁只能recursion安全。
下一次在代码中遇到死锁时,请检查每个线程上的调用堆栈,以查看是否有死锁的线程已经拥有locking。 在上面的示例代码中,通过在A,B和C点添加长时间的睡眠,可以几乎100%一致地重新创build死锁。
编辑:
我不再能够certificate上一个问题,但是有一个相关的情况仍然会导致问题。 它与dispatch_sync
的dynamic行为有关。
在这个代码中,有两次recursion获取锁的尝试。 首先从主队列调用一个后台队列。 第二个调用从后台队列进入主队列。
导致行为差异的是调度队列和线程之间的区别。 第一个例子调用一个不同的队列,但不会改变线程,所以recursion互斥量被获取。 第二个改变队列时改变线程,所以不能获取recursion互斥。
要强调的是, 这个function是devise的 ,但是对于一些不了解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(_dataLock)
做了它应该做的事情,因为它毕竟是一个基本的东西。
我继续调查_dataLock
对象,在我的devise中,我有几个Database
对象将独立执行它们的locking,所以我只是简单地为每个Database
实例创build_dataLock = [[NSNumber numberWithInt:1] retain]
。
但[NSNumber numberWithInt:1]
返回相同的对象,如同一个指针!
这意味着什么我认为是一个本地化的锁只有一个Database
实例是不是一个全局锁的Database
所有实例。
当然,这不是预期的devise,我相信这是问题的原因。
我会改变的
_dataLock = [[NSNumber numberWithInt:1] retain]
同
_dataLock = [[NSUUID UUID] UUIDString] retain]
- Xcode:UI自动化:waitForValid()
- Objective-C决定了iOS设备的数据networkingtypes
- 在CBPeripheralManager中,如何知道用户在尝试配对时是否按下取消button
- 外观代理 – setShadowImage替代iOS 5?
- 如何使用Objective-C将audio数据保存到CoreData或从CoreData获取audio数据?
- UITableview Cellexception – '必须将自动resize的掩码转换为约束才能拥有_setHostsLayoutEngine:YES
- 自动布局引擎如何计算UI组件框架。
- 如何在Xcode中设置启动屏幕图像
- MKMapView MKCircle渲染半径太大的圆