2011-01-15 17 views
5

Przeczytałem rozdział Marcusa Zarry na temat wielowątkowości w jego książce danych podstawowych i przyjrzałem się dokładnie jego przykładowemu kodowi. Ale jego kod i inne, które znalazłem gdzie indziej wydają się być skoncentrowane w procesach działających w tle, które nie muszą być świadome siebie nawzajem. Te przykłady są dobre do importowania struktury drzewa - ale nie odnoszą się do importu bardziej ogólnej (złożonej) struktury, jak na przykład skierowany wykres acykliczny.Dane podstawowe i wielowątkowość

W moim przypadku próbuję parsować hierarchię klasy C++ i chciałbym użyć jak najwięcej NSOperations, jak to możliwe. Chciałbym utworzyć wystąpienie NSManagedObject dla każdej napotkanej klasy i chciałbym scalić różne NSManagedObjectContexts, gdy jeden jest zapisany.

Na marginesie: Jestem w stanie sprawić, że wszystko działa z pojedynczą NSOperacją, która iteruje pliki i parsuje je pojedynczo. W tej implementacji -mergeChanges: podejście, które wywołuje -mergeChangesFromContextDidSaveNotification: na MOC wątku głównego działa dobrze.

Ale najlepiej byłoby, gdyby jedna operacja powtórzyła iteracje plików źródłowych i odrodziła się w NSOperations, aby przeanalizować każdy plik. Próbowałem kilku podejść - ale nie mogę tego naprawić. Najbardziej obiecujące było to, aby każda NSOperation obserwowała NSManagedObjectContextDidSaveNotification. Z -mergeChanges: wygląda tak:

- (void) mergeChanges:(NSNotification *)notification 
{ 
// If locally originated, then trigger main thread to merge. 
if ([notification object] == [self managedObjectContext]) 
    { 
    AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate]; 
    NSManagedObjectContext *mainContext = [appDelegate managedObjectContext]; 

    // Merge changes into the main context on the main thread 
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
     withObject:notification 
     waitUntilDone:YES]; 
    return; 
    } 
    // If not locally originated, then flag need to merge with in this NSOperation's thread. 
[self setNeedsToMerge:YES]; 
[self setMergeNotification:notification]; 
} 

Zasadniczo parsowania NSOperation na main() sprawdzone Ivar 'needsToMerge' okresowo. Jeśli było to prawdą, to wywoływano -mergeChangesFromContextDidSaveNotification: na lokalnym MOC z buforowanymi NSNotifications. A następnie needsToMerge został zresetowany. Jeśli powiadomienie pochodzi lokalnie, to główny wątek został poinformowany, aby wykonać -mergeChangesFromContextDidSaveNotification: na swoim MOC.

Jestem pewien, że istnieje dobry powód, dlaczego to nie działa i dlaczego mam to:

ostrzeżenie: Zakończenie połączenia - kod objc na stosie bieżący wątek sprawia to niebezpieczne.

Próbowałem również użyć blokady NSPeristentStoreCoordinator do kontroli dostępu - ale to jest problematyczne, jeśli odbywa się w trakcie rozmowy na -save NSManagedObjectContext za: Metoda bo -save: powiadomi zainteresowanych obserwatorów zapisać zdarzenie i -mergeChangesFromContextDidSaveNotification: wydaje się blokować próbę zdobycia blokady PSC.

Po prostu wydaje się, że powinno być o wiele łatwiej.

Odpowiedz

2

myślę hade ten sam problem i tutaj jest, jak go rozwiązać:

Tworzenie niestandardowego NSOperation klasy, w których można zdefiniować:

NSMutableArray * changeNotifications; 
NSLock * changeNotificationsLock; 
NSManagedObjectContext * localManagedObjectContext; 

W swojej NSOperation główną metodę przed zapisaniem kontekst najpierw zastosować wszystkie żądane zmiany:

[self.changeNotificationsLock lock]; 
for(NSNotification * change in self.changeNotifications){ 
    [self.localManagedObjectContext mergeChangesFromContextDidSaveNotification:change]; 
} 
if([self.changeNotifications count] >0){ 
    [self.changeNotifications removeAllObjects]; 
} 
[self.changeNotificationsLock unlock]; 

NSError *error = nil; 
[self.localManagedObjectContext save:&error] 

Zauważ, że użyłem zamek, to dlatego NSMutableArray nie jest bezpieczeństwo wątków i chcę, aby bezpiecznie przejść changeNotifications. changeNotifications to tablica, w której przechowywane są wszystkie zmiany, które należy zastosować przed zapisaniem kontekstu.

A oto twoja metoda scalania, zmodyfikowana tak, że wszystkie zmiany, które muszą zostać scalone przez twoją NSOperation, są scalane przy użyciu właściwego wątku. Zauważ, że to metody jest nazywany przez innych wątków niż NSOperation jednym, dlatego trzeba, aby zablokować dostęp do self.changeNotifications

- (void) mergeChanges:(NSNotification *)notification 
{ 
// If not locally originated, then add notification into change notification array 
// this notification will be treated by the NSOperation thread when needed. 
if ([notification object] != self.localManagedObjectContext) 
    { 
    [self.changeNotificationsLock lock]; 
    [self.changeNotifications addObject:notification]; 
    [self.changeNotificationsLock unlock]; 
    } 

//Here you may want to trigger the main thread to update the main context  

} 

nadzieję, że ta pomoc! Ta metoda nie jest w 100% twarda jak skała, ale w niektórych przypadkach powiadomienie o zmianie może nastąpić zbyt późno. W takim przypadku metoda zapisu kontekstowego zwróci błąd i będziesz musiał ponownie załadować obiekt NSManagedObject i zapisać go ponownie. Jeśli potrzebujesz więcej informacji, daj mi znać.

+0

To jest dobre rozwiązanie. Dla mnie prawdopodobnie musiałbym zastosować changeNotifications przed wyszukiwaniem obiektu oprócz wcześniejszego zapisu - ale to byłoby łatwe. Jak rozumiem, wszystkie wątki (z wyjątkiem wątku głównego) byłyby instancjami tej podklasy. Czy tak jest? – westsider

+0

@Wyzywanie wszystkiego, co musi scalić kontekst, powinno być jego podklasą. Zasadniczo proponuję utworzyć klasy NSOperationManagedOobjectContextAware, który ma metodę mergeChanges i 3 właściwości changeNotifications, changeNotificationsLock, localManagedObjectContext –

0

To dwa spokój kod działa poprawnie w mojej aplikacji:

- (void)mergeChanges:(NSNotification *)notification; 
{ 
//AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate]; 
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext]; 

// Merge changes into the main context on the main thread 
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
           withObject:notification 
          waitUntilDone:YES]; 
} 

-(void) main { 
// Register context with the notification center 
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
[nc addObserver:self 
     selector:@selector(mergeChanges:) 
      name:NSManagedObjectContextDidSaveNotification 
     object:managedObjectContext]; 

Oczywiście managedObjectContext myśli:

managedObjectContext = [[NSManagedObjectContext alloc] init]; 
[managedObjectContext setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]]; 
[managedObjectContext setUndoManager:nil]; 

Bądź ostrożny, jeśli u trzeba usunąć coś przed u dokonać aktualizacji z głównym moc. Mam szalony czas i wiele trudnych do debugowania błędów, a jednocześnie rozumiem, że nie mogę używać mocu z głównego wątku, podczas gdy nigdzie przetwarzam inne zmiany z tą samą zawartością.

+0

Nie widzę, jak to działałoby, aby rozwiązać mój problem. Wygląda na to, że * wszystko * scalanie zostanie wykonane w głównym wątku. To, czego szukam, to sposób na łączenie się w wielu wątkach innych niż główne, tak aby kilka wątków mogło analizować różne pliki, ale dzieliło ich przyrostowe postępy. – westsider

+0

To jest sposób na umieszczenie części analizy do głównej mocy. U musisz zrobić kod na głównym wątku później, ponieważ rozumiem twoje zadanie. – Alex

+0

Próbuję uzyskać wiele wątków odczytujących * i * zapisywanie do współużytkowanego stałego koordynatora sklepu, tak aby każdy nowo dodany obiekt był dostępny dla * wszystkich * wątków od razu po dodaniu. Różni się to od tego, że kontekst zarządzanego obiektu głównego wątku jest świadomy wszystkich nowo dodanych obiektów. – westsider

Powiązane problemy