42 votes

NSManagedObjectContext performBlockAndWait: ne s'exécute pas sur le thread d'arrière-plan?

J'ai un NSManagedObjectContext déclaré comme suit:

 - (NSManagedObjectContext *) backgroundMOC {
    if (backgroundMOC != nil) {
        return backgroundMOC;
    }
    backgroundMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    return backgroundMOC;
}
 

Notez qu'il est déclaré avec un type d'accès simultané à une file privée, de sorte que ses tâches doivent être exécutées sur un thread en arrière-plan. J'ai le code suivant:

 -(void)testThreading
{
    /* ok */
    [self.backgroundMOC performBlock:^{
        assert(![NSThread isMainThread]); 
    }];

    /* CRASH */
    [self.backgroundMOC performBlockAndWait:^{
        assert(![NSThread isMainThread]); 
    }];
}
 

Pourquoi l'appel de performBlockAndWait exécute-t-il la tâche sur le thread principal plutôt que sur le thread d'arrière-plan?

99voto

Jody Hagins Points 15644

Jeter dans une autre réponse, pour essayer d'expliquer pourquoi performBlockAndWait sera toujours exécuté dans le thread appelant.

performBlock est complètement asynchrone. Il sera toujours de mettre en file d'attente du bloc sur la file d'attente de la réception de la MOC, et les retourner immédiatement. Ainsi,

[moc performBlock:^{
    // Foo
}];
[moc performBlock:^{
    // Bar
}];

fera deux blocs sur la file d'attente pour le moc. Ils vont toujours s'exécuter de manière asynchrone. Un inconnu thread va tirer des blocs de la file d'attente et de les exécuter. En outre, ces blocs sont enveloppés à l'intérieur de leur propre autorelease pool et ils représentent une complète Base de Données de l'utilisateur (processPendingChanges).

performBlockAndWait ne PAS utiliser la file d'attente interne. C'est une opération synchrone qui s'exécute dans le contexte du thread appelant. Bien sûr, il faudra patienter jusqu'à l'exploitation courante dans la file d'attente ont été exécutées, et alors que le bloc s'exécute dans le thread appelant. Ceci est documenté (et réaffirmé dans plusieurs WWDC présentations).

En outre, performBockAndWait est ré-entrant, de sorte que les appels imbriqués, tout se fait à droite dans ce thread appelant.

La Base de Données ingénieurs ont été très clairs à ce que le thread dans lequel une file d'attente MOC opération s'exécute n'est pas important. C'est la synchronisation à l'aide de l' performBlock* API c'est la clé.

Donc, envisager de performBlock " comme "Ce bloc est placé dans une file d'attente, pour être exécuté à un certain temps indéterminé, dans certains indéterminée fil. La fonction sera de retour à l'appelant dès qu'il a été mis en file d'attente"

performBlockAndWait est "Ce bloc sera exécuté à un certain temps indéterminé, dans ce même thread. La fonction sera de retour après ce code a complètement exécuté (qui aura lieu après la file d'attente en cours associé avec ce MOC a drainé)."

MODIFIER

Êtes-vous sûr de "performBlockAndWait ne PAS utiliser la file d'attente interne"? Je pense qu'il n'. La seule différence est que performBlockAndWait va attendre jusqu'à ce que le bloc d'achèvement. Et qu'entendez-vous par appel thread? Dans ma compréhension, [moc performBlockAndWait] et [moc performBloc] tous les deux sur sa file d'attente privée (de fond ou de la main). L' concept important ici est de moc est propriétaire de la file d'attente, pas dans l'autre sens autour de. S'il vous plaît corrigez-moi si je me trompe. – Philip007

C'est dommage que j'ai formulée de la réponse comme je l'ai fait, parce que, prises par lui-même, il est incorrect. Cependant, dans le contexte de la question d'origine, il est correct. Plus précisément, lors de l'appel d' performBlockAndWait sur une file d'attente privée, le bloc à exécuter dans le thread qui a appelé la fonction, il ne sera pas mis sur la file d'attente et sont exécutés sur le "private thread."

Maintenant, avant même d'entrer dans les détails, je tiens à souligner que selon le fonctionnement interne des bibliothèques est très dangereux. Tout ce que vous devriez vraiment prendre soin, c'est que vous ne peut jamais s'attendre à un thread spécifique pour l'exécution d'un bloc, à l'exception de tout autre article pour le thread principal. Ainsi, s'attendant à un performBlockAndWait à ne pas exécuter dans le thread principal n'est pas conseillé, car il va les exécuter dans le thread qui l'a appelée.

performBlockAndWait utilise GCD, mais il a aussi son propre calque (par exemple, pour éviter les blocages). Si vous regardez le PGCD de code (qui est open source), vous pouvez voir comment les appels synchrones de travail et, en général, ils se synchronisent avec la file d'attente et appeler le bloc sur le thread qui a appelé la fonction, à moins que la file d'attente est le principal file d'attente ou un mondial de la file d'attente. Aussi, dans la WWDC pourparlers, la Base de Données ingénieurs de souligner que les performBlockAndWait sera exécuté dans le thread appelant.

Donc, quand je dis de ne pas utiliser la file d'attente interne, cela ne signifie pas qu'il n'utilise pas les structures de données à tous. Il doit synchroniser l'appel avec les blocs déjà dans la file d'attente, ainsi que ceux présentés dans d'autres fils et d'autres appels asynchrones. Cependant, lors de l'appel d' performBlockAndWait il n'a pas mis le bloc sur la file d'attente... au lieu de cela il se synchronise l'accès et exécute le soumis bloc sur le thread qui a appelé la fonction.

Maintenant, ce n'est pas un bon forum pour cela, parce que c'est un peu plus complexe que cela, surtout w.r.t la file d'attente principale, et PGCD mondial des files d'attente - mais ce dernier n'est pas important pour la Base de Données.

Le point principal est que lorsque vous appelez une performBlock* ou PGCD fonction, vous ne devriez pas attendre pour l'exécuter sur n'importe quel thread particulier (à l'exception de quelque chose lié à la thread principal) parce que les files d'attente ne sont pas les threads, et seuls les principaux file d'exécuter des blocs sur un thread spécifique.

Lors de l'appel de la base de données performBlockAndWait le bloc s'exécute dans le thread appelant (mais sera correctement synchronisées avec tout soumis à la file d'attente).

J'espère qu'un sens, même s'il est probablement causé plus de confusion.

MODIFIER

En outre, vous pouvez voir les non-dits des implications de cela, que la façon dont performBlockAndWait fournit à ré-entrant soutien des sauts de la FIFO de commande de blocs. Comme un exemple...

[context performBlockAndWait:^{
    NSLog(@"One");
    [context performBlock:^{
        NSLog(@"Two");
    }];
    [context performBlockAndWait:^{
        NSLog(@"Three");
    }];
}];

Notez que le strict respect de la FIFO de garantie de la file d'attente signifie que le sous - performBlockAndWait ("Trois") courent après le asynchrones bloc ("Deux") depuis qu'il a été déposé après le bloc async a été soumis. Cependant, ce n'est pas ce qui se passe, car il serait impossible... pour la même raison, un blocage s'ensuit avec imbriqué dispatch_sync des appels. Juste quelque chose d'être conscient de si vous utilisez la version synchrone.

En général, éviter de synchroniser les versions à chaque fois que possible, car dispatch_sync peut provoquer un blocage, et de tout ré-entrant version, comme performBlockAndWait aurez à faire une "mauvaise" décision de le soutenir... comme avoir la synchronisation des versions "sauter" la file d'attente.

3voto

borrrden Points 20950

Pourquoi pas? Grand Central Dispatch du bloc de simultanéité paradigme (qui, je suppose MOC utilise en interne) est conçue de telle façon que le moteur d'exécution et le système d'exploitation besoin de s'inquiéter à propos des threads, pas le développeur (parce que le système d'exploitation peut faire mieux que vous pouvez faire pour avoir de plus amples informations). Trop de gens supposent que les files d'attente sont les mêmes que les threads. Ils ne le sont pas.

File d'attente des blocs ne sont pas requis pour exécuter sur n'importe quel thread donné (à l'exception des blocs dans la file d'attente principale doit exécuter dans le thread principal). Donc, en fait, parfois de synchronisation (c'est à dire performBlockAndWait) en file d'attente des blocs sera exécuté sur le thread principal si le moteur d'exécution estime qu'il serait plus efficace que la création d'un thread pour ça. Puisque vous êtes en attente pour le résultat de toute façon, il ne serait pas changer la façon dont votre programme a fonctionné si le thread principal étaient suspendus pendant la durée de l'opération.

Cette dernière partie, je ne suis pas sûr si je me souviens bien, mais dans la WWDC 2011 de vidéos sur les PGCD, je crois qu'il a été mentionné que le runtime de faire un effort pour s'exécuter sur le thread principal, si possible, pour les opérations de synchronisation car il est plus efficace. Au final, cependant, je suppose que la réponse au "pourquoi" ne peut être répondu par les gens qui ont conçu le système.

0voto

Jesse Rusak Points 33702

Je ne pense pas que le MOC soit obligé d'utiliser un fil de fond; vous devez simplement vous assurer que votre code ne rencontrera pas de problèmes de simultanéité avec le MOC si vous utilisez performBlock: ou performBlockAndWait: . Puisque performBlockAndWait: est supposé bloquer le thread actuel, il semble raisonnable d’exécuter ce blocage sur ce thread.

0voto

piotrb Points 311

L' performBlockAndWait: appel seulement permet de s'assurer que vous exécutez le code de telle manière à ne pas introduire de la concurrence (c'est à dire sur les 2 threads performBlockAndWait: ne sera pas exécuté dans le même temps, ils vont bloquer les uns les autres).

Le long et le court, c'est que vous ne pouvez pas dépendre sur le thread un MOC exploitation s'exécute sur, eh bien en fait jamais. J'ai appris à la dure que si vous utilisez le PGCD ou tout droit jusqu'à la threads, vous devez toujours créer local Gpm pour chaque opération, puis de les fusionner pour le maître de MOC.

Il y a une grande bibliothèque (MagicalRecord) qui rend le processus très simple.

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