2013-08-20 49 views
11

Nasza aplikacja wydaje się przypadkowo powiesić na psynch_mutexwait. Wygląda na to, że jest związany z procesem działającym w tle, który aktualizuje wiele danych przechowywanych w CoreData - ale nie byłem w stanie dowiedzieć się, kto blokuje to, co powoduje zakleszczenie.Aplikacja zawiesza się na __psynch_mutexwait

Poniżej znajduje się pełny ślad stosu, który daje mi lldb - co jest oczywiście niepełne, a ostatnia klatka wątku 1 jest nieprawdziwa. Miałem przełom w tej metodzie kilka linijek przed tym i nigdy nie został trafiony.

Czy istnieje JAKIKOLWIEK sposób na ustalenie, na który zamek jest oczekiwany? (lub nawet uzyskać prawidłowe ślady stosu?) Oczywiście jest wiele kodu zaangażowanego, co sprawia, że ​​losowe oświadczenia NSLog są ogromnym przedsięwzięciem.

(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) 

Odpowiedz

2

To widać, gdy związane podmiotu w innym kontekście (i w innym wątku) został zmodyfikowany, ale jeszcze nie trwało.

Scenariusz:

A --> B

powodu błędu B już czeka na zmiany, w innym kontekście, w innym wątku. Bug spowodował, że B powiesił się zamiast zapisywać lub wycofywać. Próba zapisania A w bieżącym kontekście/wątku spowoduje, że oczekiwanie na inny wątek zwolni blokadę na B.

Jedynym skutecznym sposobem rozwiązywania problemów była lista wszystkich oczekujących podmiotów i porównanie z zablokowanymi wątkami. Zajęło to trochę czasu :(

ja wciąż szukają czegoś, wymienić wszystkie zamki w bazie danych i podmiotów.

12

Poprzez kilka osób spojrzeć na kod, a śladowe przez długi skomplikowanych ścieżkach kodu, okazało się co wydaje się być winowajcą. Jedna metoda działa w wątku tła było znalezienie i korzystania niektóre obiekty danych Core i korzystania z kontekstu głównego wątkach.

Pewnie byłby pomogło jeśli IOS dałoby użytecznych ślady stosu.

+2

spróbuj włączyć podstawowych danych wielowątkowych wywołać twierdzeń. Został wprowadzony w takich sytuacjach. – Mazyod

2

Zwykle dzieje się tak, gdy próbuje się uzyskać dostęp do rdzenia Obiekty danych w wątku tła używające kontekstu głównego wątków w tym samym czasie, używając tego samego kontekstu obiektu zarządzanego na różnych wątkach (w tle lub w głównym). Aby uzyskać więcej informacji, sprawdź numer Core Data concurrency rules.

Aby uniknąć obu tych przypadków, główna zasada brzmi: każdy wątek musi mieć własny kontekst obiektu zarządzanego i inicjować ten kontekst dokładnie tam, gdzie ma być używany.

Na przykład:

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

    . 
    . 
    . 

});