31 votes

Quelles sont les différentes façons d'appeler ma méthode sur un thread séparé ?

J'ai une méthode de calcul des données (que ce soit "maMéthode :"), et je veux déplacer l'appel vers un autre thread parce que je ne veux pas bloquer la fonctionnalité principale de mon interface utilisateur. J'ai donc commencé à faire quelques recherches sur la façon d'appeler ma méthode sur un autre thread. D'après ce que je vois, il y a actuellement beaucoup de façons différentes de le faire. En voici une liste :

a) utiliser des threads purs (disponible depuis iOS 2.0) :

[NSThread detachNewThreadSelector:@selector(myMethod:) toTarget:self withObject:_myParamsArray];

b) en utilisant un simple raccourci (disponible depuis iOS 2.0). Disponible à partir de NSObject hérité mais la méthode appartient aussi à la classe NSThread :

[self performSelectorInBackground:@selector(myMethod:) withObject:_myParamsArray];

c) en utilisant une nouvelle approche des files d'attente de Grand Central Dispatch (disponible depuis iOS 4.0) :

dispatch_async(dispatch_get_global_queue(0, 0),
  ^ {
      [self myMethod:_myParamsArray];
    });

d) d'une manière ou d'une autre, en utilisant des classes telles que NSOperation, NSBlockOperation ou NSOperationQueue, mais je ne sais pas exactement comment le faire (un exemple serait apprécié).

Actuellement, j'ai utilisé la casse "b" mais je suis curieux de connaître les avantages et les inconvénients et autres suggestions à ce sujet.

UPDATE : e) a également trouvé un autre moyen de réaliser des opérations similaires de threading - Boucles d'exécution . Voici un extrait de la documentation d'Apple :

Une boucle d'exécution est une boucle de traitement des événements que vous utilisez pour planifier le travail et coordonner la réception des événements entrants. L'objectif d'une boucle d'exécution est de garder votre thread occupé lorsqu'il y a du travail à faire et de le mettre en veille lorsqu'il n'y en a pas.

À mon avis, vous avez plus ou moins affaire à la même tâche - comment appeler votre méthode sur un thread séparé pour son opération asynchrone.

UPDATE2 : J'ai déjà eu quelques expériences avec NSInvocationOperation et NSOperationQueue et IMHO c'est assez pratique. D'après la documentation d'Apple, GCD et NSOperations sont les moyens préférés pour implémenter le multithreading. De plus, NSOperations fonctionne sur GCD à partir d'iOS 4.0. En bref, vous instanciez NSIvocationOperation (en tant qu'appel à votre méthode) puis vous instanciez NSOperationQueue et ajoutez l'invocation à la file d'attente. NSOperationQueue est assez intelligent, vous pouvez instancier plusieurs objets NSIvocationOperation (enveloppant vos appels de méthode) et les ajouter à NSOperationQueue. Le reste est assuré. NSOperationQueue détermine le nombre de threads parallèles dont il a besoin pour effectuer les appels (NSInvocationOperation) et s'en occupe pour vous. Il se peut qu'elle exécute le premier appel sur le thread A, puis le deuxième sur le thread B, le troisième sur le thread C et le quatrième sur le thread B, vous n'avez donc pas à vous en soucier. Mais si vous voulez, vous pouvez indiquer le nombre maximum de threads que NSOperationQueue peut utiliser pour exécuter les appels (par exemple 1) mais je n'en ai pas besoin. Par défaut, toutes les tâches sont effectuées sur un thread autre que le thread principal, donc les files d'opérations sont asynchrones par défaut. En outre, si vous souhaitez effectuer vos appels de méthode (chacun enveloppé dans une NSInvocationOperation distincte) dans une file d'attente stricte, vous pouvez ajouter des dépendances et ainsi NSOperationQueue préservera l'ordre des appels de méthode. Voici un exemple :

// wrap your method call into NSInvocationOperation object
NSInvocationOperation *currentOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(yourMethodCall) object:nil];

// _sharedOperationQueue is a shared NSOperationQueue 
// get all executing operations from the queue and get the last operation
_lastOperation = [[_sharedOperationQueue operations] lastObject];

// check if _lastOperation is not nil
if (_lastOperation) {

    // if not then add dependency, so the calls would be performed in a queue
    [currentOperation addDependency:_lastOperation];
}

// say - execute my method (operation)
[_sharedOperationQueue addOperation:currentOperation];

_lastOperation = currentOperation; // mark as last operation for adding dependency to the next operation

// the queue will retain invocation operation so you will release
[currentOperation release];

 ..... you can create another NSInvocationOperation and add it to the queue....

En ce qui concerne les RUNLOOPs, vous y serez tout de même parfois confronté, par exemple lors du lancement/de la programmation d'une minuterie, ou lors de l'établissement de connexions NSURL. IMHO, un runloop peut être comparé à une file d'attente de tâches exécutées sur un thread. Un runloop est un pointeur vers un thread qui fonctionne comme une file d'attente : il contient des tâches qui peuvent lancer des événements et elles seront placées à la fin de la file d'attente dans ce thread. Par défaut, toutes les tâches de votre application s'exécutent dans un seul runloop - dans un seul thread. Je dis qu'il s'agit d'un pointeur car lorsque votre application génère des événements, elle doit savoir où placer cet événement (événement tactile ou autre rappel de délégué) pour qu'il soit exécuté. Bien entendu, vous devriez lire le document sur les boucles d'exécution pour obtenir des informations plus détaillées, car ce ne sont que mes réflexions.

25voto

Macmade Points 27414

En général, vous préférez l'approche GCD.

Il est plus simple en matière de synchronisation/verrouillage que les threads purs (NSThread - pthread), et il peut être plus précis du point de vue des performances.

Lorsque vous utilisez des threads purs, le problème est que vous pouvez avoir des problèmes de performance, en fonction du nombre de cœurs/processeurs disponibles.

Par exemple, si vous ne disposez que d'un seul cœur, la création de nombreux threads peut ralentir votre application, car le CPU passera la plupart de son temps à passer d'un thread à l'autre, à sauvegarder la pile, les registres, etc.

D'un autre côté, si vous disposez d'un grand nombre de cœurs, il peut être intéressant de créer un grand nombre de threads différents.

C'est là que le GCD intervient, car il gère cela pour vous. Il créera le nombre approprié de threads, en fonction des ressources système disponibles, afin de garantir une utilisation optimale et de planifier vos actions en conséquence.

Cependant, pour cette raison, les tâches lancées avec GCD peuvent ne pas être en temps réel.

Donc si vous avez VRAIMENT besoin qu'une tâche détachée s'exécute immédiatement, utilisez un thread explicite. Sinon, utilisez GCD.

J'espère que cela vous aidera : )

EDIT

Une remarque sur performSelectorInBackground il crée simplement un nouveau fil. Il n'y a donc pas de différence avec l'approche NSThread.

EDIT 2

Les choses liées à la NSOperation sont un peu différentes. Sous Mac OS X, elles sont implémentées à l'aide de GCD depuis la version 10.6. Les versions précédentes utilisent les threads.

Sur iOS, ils sont mis en œuvre en utilisant uniquement des threads.

Référence

Tout cela est très bien expliqué dans le Guide de programmation en concomitance . Il aborde les approches GCD et thread, avec de nombreux détails sur les utilisations et les implémentations.

Si vous ne l'avez pas encore lu, vous devriez y jeter un coup d'œil.

0voto

drkibitz Points 195

Voici une réponse actualisée à cette vieille question. Les NSOperations sont implémentées avec libdispatch pour macos et ios maintenant. Elles le sont depuis un certain temps. Le meilleur moyen d'appeler un autre thread est d'utiliser libdispatch ou NSOperations. L'idée principale à garder à l'esprit est la "qualité de service", ou "QOS". Une QOS est quelque chose d'assigné à un thread, et quand vous l'assignez à une file d'attente de distribution, une NSOperationQueueue, ou une NSOperation, vous dites essentiellement, "Je veux que ceci soit exécuté sur un thread avec cette QOS assignée". Le cas particulier est le thread principal.

Il peut encore y avoir des cas pour utiliser NSThread, mais je n'en ai pas eu depuis longtemps.

0 votes

Il aurait été plus approprié de commenter la réponse précédente, mais je suis encore en train de m'habituer à l'étiquette de stackoverflow.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X