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. 线程1进入-printEight并获取locking。
  2. 线程2input-printSomethingElse并尝试获取该锁。 锁由线程1保持,因此它被排队等待,直到锁可用并阻塞。
  3. 线程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]