2009-10-27 5 views
2

J'ai mon délégué d'application principal qui contient une méthode qui retourne un objet. Ce délégué d'application s'exécute sur le thread principal. J'ai également une NSOperation qui s'exécute sur un thread différent. En plus de vouloir parfois appeler ma méthode de délégué d'application sur mon thread principal, je dois également l'appeler depuis mon thread NSOperation pour obtenir l'objet qu'il retourne. Ma première question est, si j'appelle cela de mon autre fils ...Question multi-thread dans Objective-C 2.0

id newObject = [[[UIApplication sharedApplication] delegate] myMethod]; 

... cette méthode sera traiter sur le même fil que le NSOperation, ou ce que ce sera le même fil (principal) que le délégué de l'application est activé?

Je veux également m'assurer que le code dans myMethod est seulement appelé une fois à la fois par mon fil d'opération ou mon fil principal. Puis-je simplement créer une instance NSLock var dans mon délégué d'application et faire quelque chose comme:

-(id)myMethod { 
    [myLock lock]; 
    myObject = // Get or create my object to return 
    [myLock unlock]; 
    return myObject; 
} 

Merci pour votre aide!

Mike

Répondre

11

À moins que vous écrivez du code explicitement faire quelque chose à exécuter sur un autre thread, chaque appel de méthode va être exécutée directement sur le fil, il a été appelé. Il n'y a pas de magie avec un appel de méthode. Vous pouvez le considérer comme ayant exactement la même sémantique/ABI qu'un appel de fonction C aux fins de l'enfilage. Votre modèle de verrouillage fonctionne correctement pour assurer un accès exclusif à travers les filetages.

Deux notes sans rapport avec d'autres (parce que beaucoup de gens trébuchent dessus):

  • déclarant une propriété comme atomic n'a rien à voir avec la sécurité des threads. Atomicity garantit seulement que vous obtenez une valeur valide, pas la valeur correcte (il y a une différence).

  • Les objets à libération automatique ne sont jamais sûrs de passer entre les threads. Vous avez besoin d'un retain explicite sur le thread d'envoi et d'un éventuel release d'équilibrage sur le thread de réception.

+0

Merci pour votre réponse utile :-) –

+1

Serait-il vrai de dire qu'un bloc @synchronized (auto) {} ferait la même chose que une instance NSLock? –

+0

Plus ou moins; les détails de mise en œuvre sont légèrement différents, mais l'impact est le même. – bbum

3

Est-ce que vous avez absolument besoin d'effectuer cette invocation sur le fil NSOperation, et ne pas fournir simplement l'objet requis dans le cadre de la création de l'opération personnalisée?

Si c'est le cas, je recommanderais de ne pas utiliser de verrous sauf si les performances sont critiques. Si l'iPhone pris en charge, vous pouvez utiliser Grand Central Dispatch pour obtenir l'objet sur votre fil:

__block id newObject = nil; 
dispatch_sync(dispatch_get_main_queue(), ^{ 
    newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain]; 
}); 

Pour l'iPhone, je serais tenté de créer une méthode d'assistance:

- (void)createNewObject:(NSValue *)returnPtr { 
    id newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain]; 
    *(id *)[returnPtr pointerValue] = newObject; 
} 

Et invoquer comme ceci de votre NSOperation fil:

id newObject = nil; 
[self performSelectorOnMainThread:@selector(createNewObject:) 
         withObject:[NSValue valueWithPointer:&newObject] 
        waitUntilDone:YES]; 

en fait effectuer l'exécution sur le thread principal a moins de risques implicites.

+0

C'est une approche intéressante. J'ai remarqué que la méthode performSelectorOnMainThread ne vous permet pas de retourner une valeur, mais je n'ai jamais pensé à passer l'adresse d'un pointeur! Par intérêt, que fait le casting * (id *)? J'ai vu un casting tel que (NSNumber *) mais je pensais que le type d'id n'avait pas besoin d'un pointeur astérix. Et quel est le premier astérix? –

+0

Si vous voulez vraiment faire des blocs/GCD sur iPhone, consultez les projets PLBlocks et WiganWallgate. – nall

+0

Le cast '(id *)' s'assure que le 'void *' renvoyé par la méthode '-pointerValue' est traité comme un pointeur vers un' id'. Le premier '*' le déréférence, de sorte que vous modifiez la cible du pointeur plutôt que le pointeur lui-même. –

2

Si vous avez simplement besoin de protéger une section de code critique, pourquoi ne pas utiliser la directive Objective-C @synchronized? Bien sûr, l'utilisation de NSLock fonctionnera également, mais vous devez gérer explicitement l'instance NSLock. De la documentation:

Objective-C prend en charge le multithreading dans les applications. Cela signifie que deux threads peuvent essayer de modifier le même objet en même temps, ce qui peut causer de sérieux problèmes dans un programme. Pour éviter que des sections de code ne soient exécutées par plus d'un thread à la fois, Objective-C fournit la directive @synchronized().

La directive @synchronized() verrouille une section de code à utiliser par un seul thread. Les autres threads sont bloqués jusqu'à ce que le thread quitte le code protégé; c'est-à-dire, lorsque l'exécution continue après la dernière instruction dans le bloc @synchronized().

La directive @synchronized() prend comme seul argument tout objet Objective-C, y compris self. Cet objet est connu sous le nom de sémaphore d'exclusion mutuelle ou mutex. Il permet à un thread de verrouiller une section de code pour empêcher son utilisation par d'autres threads. Vous devez utiliser des sémaphores distincts pour protéger les différentes sections critiques d'un programme. Il est plus sûr de créer tous les objets d'exclusion mutuelle avant que l'application ne devienne multithread pour éviter les conditions de course.

La liste 12-1 montre un exemple de code qui utilise self comme mutex pour synchroniser l'accès aux méthodes d'instance de l'objet courant. Vous pouvez adopter une approche similaire pour synchroniser les méthodes de classe de la classe associée, en utilisant l'objet Class au lieu de self. Dans ce dernier cas, bien sûr, un seul thread à la fois est autorisé à exécuter une méthode de classe car il n'y a qu'un seul objet de classe partagé par tous les appelants.

Listing 12-1 verrouillage d'une méthode utilisant l'auto

- (void)criticalMethod 
{ 
    @synchronized(self) { 
     // Critical code. 
     ... 
    } 
} 
+0

Cela semble très prometteur. La seule chose que je ne comprends pas bien est l'objet de sémaphonie que vous lui passez. Je ne comprends pas à quoi ça sert!? –