应用挂在__psynch_mutexwait
我们的应用程序似乎半随机挂在psynch_mutexwait。 这似乎与更新CoreData中存储的一堆数据的后台进程有关 – 但是我完全无法弄清楚是谁locking了造成死锁的原因。
以下是lldb给我的完整堆栈跟踪 – 显然是不完整的,并且线程1的最后一帧是虚假的。 在那之前,我在这个方法中有一个断点,它从来没有被打过。
是否有任何方法来确定什么锁正在等待? (甚至可以得到正确的堆栈跟踪?)当然,涉及的代码很多,这使得随机的NSLog语句成为一个巨大的工作。
(lldb) bt all * thread #1: tid = 0x2503, 0x39da20fc libsystem_kernel.dylib`__psynch_mutexwait + 24, stop reason = signal SIGSTOP frame #0: 0x39da20fc libsystem_kernel.dylib`__psynch_mutexwait + 24 frame #1: 0x39ceb128 libsystem_c.dylib`pthread_mutex_lock + 392 frame #2: 0x00022068 OnDeck`-[AttendanceWorkoutsController buildTable](self=0x00000003, _cmd=0x00000000) + 508 at AttendanceWorkoutsController.m:100 thread #2: tid = 0x2803, 0x39d92648 libsystem_kernel.dylib`kevent64 + 24 frame #0: 0x39d92648 libsystem_kernel.dylib`kevent64 + 24 frame #1: 0x39ccb4f0 libdispatch.dylib`_dispatch_mgr_invoke + 796 thread #5: tid = 0x2b03, 0x39d91eb4 libsystem_kernel.dylib`mach_msg_trap + 20 frame #0: 0x39d91eb4 libsystem_kernel.dylib`mach_msg_trap + 20 frame #1: 0x39d9204c libsystem_kernel.dylib`mach_msg + 40 thread #6: tid = 0x242f, 0x39d91eb4 libsystem_kernel.dylib`mach_msg_trap + 20 frame #0: 0x39d91eb4 libsystem_kernel.dylib`mach_msg_trap + 20 frame #1: 0x39d9204c libsystem_kernel.dylib`mach_msg + 40 thread #7: tid = 0x2c03, 0x39da2594 libsystem_kernel.dylib`select$DARWIN_EXTSN + 20 frame #0: 0x39da2594 libsystem_kernel.dylib`select$DARWIN_EXTSN + 20 frame #1: 0x31bff1f6 CoreFoundation`__CFSocketManager + 678 thread #8: tid = 0x2d03, 0x39da2d98 libsystem_kernel.dylib`__workq_kernreturn + 8 frame #0: 0x39da2d98 libsystem_kernel.dylib`__workq_kernreturn + 8 frame #1: 0x39cf0cfa libsystem_c.dylib`_pthread_workq_return + 18 (lldb)
通过让几个人看代码,并通过冗长复杂的代码path来追踪,我们发现了什么似乎是罪魁祸首。 在后台线程中运行的一种方法是查找并使用一些Core Data对象并使用主线程上下文。
如果IOS会提供有用的堆栈跟踪,肯定会有帮助。
当另一个上下文(和另一个线程上)的相关实体已经被修改但尚未被保持时,就已经看到了这种情况。
场景:
A --> B
由于错误B
有未决的变化,在另一个上下文中,在另一个线程。 这个Bug导致B
挂起而不是保存或者回滚。 尝试在当前上下文/线程中保存A
将会导致等待另一个线程释放B
上的锁。
只有排除故障的成功方法是列出所有挂起的实体,并与阻塞的线程中的实体进行比较。 花了一段时间:(
我仍然在寻找列出数据库和实体上的所有锁的东西。
这通常发生在使用主线程上下文访问后台线程上的Core Data对象或者同时在不同线程(背景或主)上使用相同的托pipe对象上下文时。 有关更多详细信息,请查看核心数据并发规则 。
所以为了避免这两种情况,主要规则是,每个线程都必须有自己的托pipe对象上下文,并将该上下文准确地初始化到将要使用的地方。
例如:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // // Prepare your background core data context // if (self.privateContext == nil) { self.privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [self.privateContext setParentContext: - main managed object context - ]; [self.privateContext setUndoManager:nil]; // this context should not manage undo actions. } // // Do any Core Data requests using this thread-save context // . . . });