Приложение зависает в __psynch_mutexwait

Наше приложение, похоже, полу-беспорядочно висеть на psynch_mutexwait. Похоже, что это связано с фоновым процессом, который обновляет кучу данных, хранящихся в CoreData, но я полностью не мог понять, кто блокирует то, что вызывает тупик.

Ниже приведена полная трассировка стека, которую lldb дает мне, что явно неполно, И последний кадр Thread 1 является фиктивным. У меня была точка останова в этом методе несколько строк до этого, и она никогда не попадалась.

Существует ли какой-либо способ выяснить, на какой замок ждали? (или даже получить правильные трассировки стека?) Конечно, есть LOTS кода, который делает случайные заявления 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) 

Ответ 1

Когда несколько человек смотрят на код и прослеживают длинные сложные пути кода, мы обнаружили, что, по-видимому, был виновником. Один метод, выполняющийся в фоновом потоке, заключался в поиске и использовании некоторых объектов Core Data и использовании контекста основных потоков.

Конечно, это помогло бы LOT, если IOS предоставит полезные трассировки стека.

Ответ 2

Это видно, когда связанный объект в другом контексте (и в другом потоке) был изменен, но еще не сохранен.

Сценарий:

A --> B

Из-за ошибки B были ожидающие изменения, в другом контексте, в другом потоке. Ошибка, из-за которой B зависала, вместо того, чтобы сохранять или катить ее обратно. Попытка сохранить A в текущем контексте/потоке вызовет ожидание того, что другой поток освободит блокировку на B.

Только успешный способ устранения проблем состоял в том, чтобы перечислить все ожидающие объекты и сравнить их с заблокированными потоками. Взял время: (

Я все еще ищу что-то, что перечисляет все блокировки в базе данных и сущностях.

Ответ 3

Обычно это происходит, когда вы пытаетесь получить доступ к объектам Core Data в фоновом потоке, используя контекст основного потока ИЛИ, используя один и тот же контекст управляемого объекта для разных потоков (фона или основного) в то же время, Подробнее см. Основные данные concurrency правила.

Таким образом, чтобы избежать обоих случаев, основное правило заключается в том, что каждый поток должен иметь свой собственный контекст управляемого объекта и инициализировать этот контекст именно там, где он будет использоваться.

Например:

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
   //

   .
   .
   .

});