Comment envoyer des blocs de code au même thread dans iOS?

Aspect principal de la question: Il s'agit d'iOS. Puis-je d'une manière ou d'une autre envoyer des blocs de code d'une manière, qu'ils vont tous (a) s'exécuter en arrière-plan et (b) sur le même thread? Je veux exécuter des opérations fastidieuses en arrière-plan, mais celles-ci doivent être exécutées sur le même thread, car elles impliquent des ressources qui ne doivent pas être partagées entre les threads.

D'autres détails techniques, si nécessaire: Il s'agit de mettre en œuvre un plugin sqlite pour Apache Cordova, un framework pour les applications HTML5 sur les plates-forms mobiles. Ce plugin devrait être une implémentation de WebSQL dans les moyens de l'API plugin de Cordova. (Cela signifie qu'il n'est pas possible d'emballer des transactions entières dans des blocs individuels, ce qui pourrait rendre tout plus facile.)

Voici un code de Cordova's Docs:

- (void)myPluginMethod:(CDVInvokedUrlCommand*)command { // Check command.arguments here. [self.commandDelegate runInBackground:^{ NSSsortingng* payload = nil; // Some blocking logic... CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsSsortingng:payload]; // The sendPluginResult method is thread-safe. [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } 

Mais pour autant que je sache, il n'y a aucune garantie, que ces blocs de code envoyés (voir runInBackground ) s'exécuteront sur le même thread.

GCD ne garantit pas que deux blocs s'exécutent sur le même thread, même s'ils appartiennent à la même queue (à l'exception de la queue principale, bien sûr). Cependant, si vous utilisez une queue série ( DISPATCH_QUEUE_SERIAL ) ce n'est pas un problème car vous savez alors qu'il n'y a pas d'access simultané à vos données.

La page de manuel de dispatch_queue_create dit:

Les files d'attente ne sont liées à aucun fil d'exécution spécifique et les blocs soumis à des files d'attente indépendantes peuvent s'exécuter simultanément.

Je ne connais aucun moyen de lier une queue à un thread spécifique (après tout, ne pas avoir à se soucier des threads est un point majeur de GCD). La raison pour laquelle vous pouvez utiliser une queue en série sans vous soucier du thread réel est cette promise:

Toutes les écritures de memory effectuées par un bloc dissortingbué vers une queue série sont garanties pour être visibles par les blocs suivants dissortingbués dans la même queue.

Autrement dit, une barrière de memory semble être utilisée.

Lorsque vous traitez des problèmes de threading, votre principale préoccupation est généralement d'éviter que deux threads accèdent à quelque chose en même time. Si vous utilisez une queue série, vous n'avez pas ce problème. Généralement, peu importe le thread qui accède à vos ressources. Par exemple, nous utilisons des files d'attente en série pour gérer l'access aux données de base sans problème.

Modifier:

Il semble que vous avez vraiment trouvé un cas rare où vous devez travailler sur le même fil. Vous pouvez implémenter votre propre thread de travail:

  • Conditions préalables:
    • Un NSMutableArray (appelons-le blockQueue ).
    • Un NSCondition (appelons-le queueCondition ).
  • Créez un nouveau NSThread.
    • La méthode du thread a une boucle sans fin dans laquelle il verrouille la condition, l'attend si la queue est vide (et le boolean "quit" est faux), supprime un bloc et l'exécute.
  • Une méthode qui verrouille la condition et met en queue le bloc.

En raison de la condition, le fil va simplement dormir alors qu'il n'y a pas de travail à faire.

Donc, à peu près (non testé, en supposant ARC):

 - (void)startWorkerThread { workerThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil ]; [workerThread start]; } - (void)threadMain { void (^block)(); NSThread *currentThread; currentThread = [NSThread currentThread]; while (1) { [queueCondition lock]; { while ([blockQueue count] == 0 && ![currentThread isCancelled]) { [queueCondition wait]; } if ([currentThread isCancelled]) { [queueCondition unlock]; return; } block = [blockQueue objectAtIndex:0]; [blockQueue removeObjectAtIndex:0]; } [queueCondition unlock]; // Execute block outside the condition, since it's also a lock! // We want to give other threads the possibility to enqueue // a new block while we're executing a block. block(); } } - (void)enqueue:(void(^)())block { [queueCondition lock]; { // Copy the block! IIRC you'll get strange things or // even crashes if you don't. [blockQueue addObject:[block copy]]; [queueCondition signal]; } [queueCondition unlock]; } - (void)stopThread { [queueCondition lock]; { [workerThread cancel]; [queueCondition signal]; } [queueCondition unlock]; } 

Dans GCD: non, ce n'est pas possible avec le dispatch lib actuel.

Les blocs peuvent être exécutés par dispatch lib sur n'importe quel thread disponible, quelle que soit la queue qui leur a été dissortingbuée.

Une exception est la queue principale, qui exécute toujours ses blocs sur le thread principal.

S'il vous plaît déposer une request de fonctionnalité à Apple, car il semble justifié et solide. Mais je crains que ce ne soit pas faisable, sinon ça existerait déjà;)

Vous pouvez utiliser NSOperationQueue . Vous pouvez le faire utiliser un seul thread en utilisant la méthode - (void)setMaxConcurrentOperationCount:(NSInteger)count . Réglez-le sur 1

Créez une queue de répartition en série et envoyez tous les appels à cette queue de répartition en série. Tous les appels seront effectués en arrière-plan, mais de manière séquentielle sur le même fil.

Si vous voulez exécuter un sélecteur dans le thread principal, vous pouvez utiliser

 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait 

et si vous voulez le faire en arrière-plan

 - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)object 

et si vous voulez jouer dans n'importe quel autre thread utilisez GCD ( Grand Central Dispatch )

 double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ //code to be executed on the main queue after delay }); 

Vous pouvez soit utiliser NSOperationQueue avec un MaxConcurrentOperationCount 1 soit aller manuellement à l'aide NSThread model NSThread (plutôt que Grand Central Dispatch). En utilisant ce dernier, je vous recommand d'implémenter une méthode worker qui s'exécute dans un thread et qui tire des work-packages (ou des commands) d'une queue ou d'un pool qui est en train d'être alimenté depuis l'extérieur du thread. Assurez-vous simplement d'utiliser Verrous / Mutex / Synchronisation.

Juste comme ça,

 dispatch_asyn(dispatch_get_current_queue, ^ { }); 

Je n'ai jamais essayé cela mais cela pourrait faire l'affaire. Utilisez des propriétés séparées des files d'attente d'envoi atomic pour chaque opération.

  @property (strong, atomic) dispatch_queue_t downloadQueue; 

File d'attente / Fil 1 pour la première opération

  downloadQueue = dispatch_queue_create("operation1", NULL); 

etc.

Comme atomic est thread-safe, downloadQueue ne doit pas être accessible par d'autres threads. Il s'assure donc qu'il n'y aura qu'un seul thread par opération et que les autres threads n'y accèderont pas.