2012-04-21 10 views
8

Jestem dispatch_sync() "blokowanie w głównej kolejce. W tym bloku ostatecznie tworzone jest połączenie z executeFetchRequest:error:. Czasami powoduje to impas.Zakleszczenie między dispatch_sync w głównej kolejce i executeFetchRequest: error:

Oto gwintu 1, który pokazuje blok wywoływana w głównym wątku, a następnie wywołanie executeFetchRequest:error:

#0 0x981f3876 in __psynch_mutexwait() 
#1 0x97a016af in pthread_mutex_lock() 
#2 0x0135be32 in -[_PFLock lock]() 
#3 0x0135be0a in -[NSPersistentStoreCoordinator lock]() 
#4 0x01371d1c in -[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore]() 
#5 0x013702c0 in -[NSManagedObjectContext executeFetchRequest:error:]() 
#6 0x0000a701 in -[NSManagedObjectContext(Convenience) fetchObjectsForEntityName:onlyIDs:withPredicate:] at /Users/mike/Dropbox/src/Tracky-iPhone/Tracky/Tracky/NSManagedObjectContext(Convenience).m:50 
#7 0x00065270 in +[NSManagedObject(ContextAdditions) contextForID:managedObjectContext:] at /Users/mike/Dropbox/src/Tracky-iPhone/Tracky/Tracky/NSManagedObject+ContextAdditions.m:40 
#8 0x0006597e in __85+[NSManagedObject(ContextAdditions) createContextIfNeededForID:managedObjectContext:]_block_invoke_0 at /Users/mike/Dropbox/src/Tracky-iPhone/Tracky/Tracky/NSManagedObject+ContextAdditions.m:55 
#9 0x00065aad in __85+[NSManagedObject(ContextAdditions) createContextIfNeededForID:managedObjectContext:]_block_invoke_050 at /Users/mike/Dropbox/src/Tracky-iPhone/Tracky/Tracky/NSManagedObject+ContextAdditions.m:64 
#10 0x023cf8d9 in _dispatch_barrier_sync_f_slow_invoke() 
#11 0x023d0509 in _dispatch_main_queue_callback_4CF() 
#12 0x01ae9803 in __CFRunLoopRun() 
#13 0x01ae8d84 in CFRunLoopRunSpecific() 
#14 0x01ae8c9b in CFRunLoopRunInMode() 
#15 0x019b47d8 in GSEventRunModal() 
#16 0x019b488a in GSEventRun() 
#17 0x004ba626 in UIApplicationMain() 

widzę, że executeFetchRequest:error: blokuje na zamek NSPersistentStoreCoordinator „s, ale nie wiem kto obecnie jest zablokowany.

Oryginalny dispatch_sync() występuje z innego wątku, a oto, że w przypadku ważne:

Thread 18, Queue : (null) 
#0 0x981f1c5e in semaphore_wait_trap() 
#1 0x023d1bda in _dispatch_thread_semaphore_wait() 
#2 0x023d0cb2 in _dispatch_barrier_sync_f_slow() 
#3 0x023d0e0f in dispatch_barrier_sync_f() 
#4 0x023d0f4c in _dispatch_sync_slow() 
#5 0x0006563a in +[NSManagedObject(ContextAdditions) createContextIfNeededForID:managedObjectContext:] at /Users/mike/Dropbox/src/Tracky-iPhone/Tracky/Tracky/NSManagedObject+ContextAdditions.m:63 
#6 0x0006e2ee in __109-[ItemFetcher createOrConfigureObjectWithDescriptor:withContext:jsonObjectIDKey:modelObjectIDKey:entityName:]_block_invoke_0 at /Users/mike/Dropbox/src/Tracky-iPhone/Tracky/Tracky/ItemFetcher.m:78 
#7 0x023ce330 in _dispatch_call_block_and_release() 
#8 0x023cff0c in _dispatch_queue_drain() 
#9 0x023cfcb4 in _dispatch_queue_invoke() 
#10 0x023cf402 in _dispatch_worker_thread2() 
#11 0x97a04b24 in _pthread_wqthread() 

Oto kod do oryginalnego wysyłki:

+ (Context *) createContextIfNeededForID: (NSString *) contextID managedObjectContext:(NSManagedObjectContext *) moc 
    { 
    // See if this context is in the main MOC. This call needs to happen synchronously on the main queue, if we're 
    // not on the main queue 
    Context * (^contextFromMainMOCBlock)(void) = 
     ^`Context` * { 
     // We are guaranteed to be in the main here; look at how this block is invoked. 
     return [self contextForID:contextID managedObjectContext:[UIApplication trackyAppDelegate].managedObjectContext] ; 
     } ; 

    __block Context *contextFromMainMOC = nil ; 

    if([UIApplication trackyAppDelegate].managedObjectContext == moc) 
     contextFromMainMOC = contextFromMainMOCBlock() ; 
    else 
     dispatch_sync(dispatch_get_main_queue(), ^{ 
      contextFromMainMOC = contextFromMainMOCBlock() ; // <-- here 
      }) ; 
… 

contextForID:contextID managedObjectContext: naprawdę robi” rób cokolwiek z danymi podstawowymi, dopóki nie zadzwoni executeFetchRequest:error:.

UPDATE

Oto pozostałe ślady stosu. AFAIK nie robią nic ciekawego, chociaż mogę się mylić.

Thread 3, Queue : (null) 
#0 0x981f490a in kevent() 
#1 0x023d0372 in _dispatch_mgr_invoke() 
#2 0x023cebe1 in _dispatch_mgr_thread() 
Thread 5 WebThread, Queue : (null) 
#0 0x981f1c22 in mach_msg_trap() 
#1 0x981f11f6 in mach_msg() 
#2 0x01b8610a in __CFRunLoopServiceMachPort() 
#3 0x01ae95d5 in __CFRunLoopRun() 
#4 0x01ae8d84 in CFRunLoopRunSpecific() 
#5 0x01ae8c9b in CFRunLoopRunInMode() 
#6 0x04776420 in _ZL12RunWebThreadPv() 
#7 0x97a02ed9 in _pthread_start() 
Thread 6 com.apple.NSURLConnectionLoader, Queue : (null) 
#0 0x981f1c22 in mach_msg_trap() 
#1 0x981f11f6 in mach_msg() 
#2 0x01b8610a in __CFRunLoopServiceMachPort() 
#3 0x01ae95d5 in __CFRunLoopRun() 
#4 0x01ae8d84 in CFRunLoopRunSpecific() 
#5 0x01ae8c9b in CFRunLoopRunInMode() 
#6 0x00ebae30 in +[NSURLConnection(Loader) _resourceLoadLoop:]() 
#7 0x00dcc4d6 in -[NSThread main]() 
#8 0x00dcc447 in __NSThread__main__() 
#9 0x97a02ed9 in _pthread_start() 
Thread 10 com.apple.CFSocket.private, Queue : (null) 
#0 0x981f3b42 in select$DARWIN_EXTSN() 
#1 0x01b1a7cb in __CFSocketManager() 
#2 0x97a02ed9 in _pthread_start() 

A oto contextID:managedObjectContext: który jest metoda zwana w dispatch_sync():

+ (Context *) contextForID: (NSString *) contextID managedObjectContext:(NSManagedObjectContext *) moc 
    { 
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"cid == %@", contextID] ; 
    NSSet *contexts = [moc fetchObjectsForEntityName:@"Context" onlyIDs:NO withPredicate:predicate] ; 

    // Integrity check 
    NSAssert1(contexts.count < 2, @"More than one context with the same CID exists: %@" , contexts) ; 

    return [contexts anyObject] ; 
    } 

fetchObjectsForEntityName:onlyIDs:withPredicate: wygląda następująco:

- (NSSet *)fetchObjectsForEntityName:(NSString *)newEntityName onlyIDs: (BOOL) onlyIDs 
    withPredicate:(id)stringOrPredicate, ... 
    { 
    NSEntityDescription *entity = [NSEntityDescription 
     entityForName:newEntityName inManagedObjectContext:self]; 

    NSAssert1(entity != nil , @"entity not found for \"%@\"" , newEntityName) ; 

    NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
    [request setEntity:entity]; 
    [request setIncludesPropertyValues:!onlyIDs] ; 

    if (stringOrPredicate) 
     { 
     NSPredicate *predicate; 
     if ([stringOrPredicate isKindOfClass:[NSString class]]) 
      { 
      va_list variadicArguments; 
      va_start(variadicArguments, stringOrPredicate); 
      predicate = [NSPredicate predicateWithFormat:stringOrPredicate 
       arguments:variadicArguments]; 
      va_end(variadicArguments); 
      } 
     else 
      { 
      NSAssert2([stringOrPredicate isKindOfClass:[NSPredicate class]], 
       @"Second parameter passed to %s is of unexpected class %@", 
       sel_getName(_cmd), NSStringFromClass(stringOrPredicate)); 
      predicate = (NSPredicate *)stringOrPredicate; 
      } 
     [request setPredicate:predicate]; 
     } 

    NSError *error = nil; 
    NSArray *results ; 
    @try { 
     results = [self executeFetchRequest:request error:&error]; 
    } 
    @catch (NSException *exception) { 
     NSLog(@"Exception caught: %@" , exception) ; 
    } 

    if (error != nil) 
     { 
     [NSException raise:NSGenericException format:@"%@",[error description]]; 
     } 

    return [NSSet setWithArray:results]; 
    } 
+4

Spróbuj 'dispatch_async()' następnie ten kod nie będzie siedzieć czekając –

+1

Jakie są inne wątki robi? Ponadto, zgadzam się z Paulem nieco. Zgodnie z filozofią GCD należy unikać synchronicznej wysyłki, kiedy tylko jest to możliwe. Problem polega na tym, że zaprojektowałeś interfejs API, który zwraca dane do swojego wywołującego, który następnie działa na nie, więc musi być synchroniczny. Zamiast tego powinien istnieć blok do przetwarzania danych, gdy jest dostępny. Dzwoniący może to przekazać, dzięki czemu cała operacja będzie asynchroniczna. –

+0

Zgadzam się również z obojgiem.Kod został pierwotnie napisany bez bloków i GCD, a jeśli to zadziała, zachowa większość interfejsów w stanie nienaruszonym. Ale może to zajmie ogromny środek. Zabawa. – leftspin

Odpowiedz

0

Aby dać więcej pomocy, trzeba pokazać kod dla wszystkich metod w śladach stosu wraz z numerami linii. W ten sposób może być skorelowany.

Wygląda na to, że masz wiele wątków. Myślę, że będziesz musiał zbadać innych, aby zobaczyć, co się dzieje.

Musisz być bardzo ostrożny podczas wywoływania operacji synchronizacji, szczególnie jeśli może być wywołana z innych operacji synchronizacji.

0

Sprawdź swoją NSManagedObjectContext dla NSManagedObjectContext dla . Wygląda na to, że twój kontekst ma NSMainQueueConcurrencyType, który uruchamia wszystkie żądania w głównym wątku. Od Apple reference:

NSMainQueueConcurrencyType

Specifies that the context will be associated with the main queue.

Jeśli mam rację trzeba zmienić typ na NSPrivateQueueConcurrencyType

Powiązane problemy