GCD pour effectuer la tâche dans le thread principal

255

J'ai un rappel qui peut provenir de n'importe quel fil. Lorsque je reçois ce rappel, je voudrais effectuer une certaine tâche sur le thread principal.

Dois-je vérifier si je suis déjà sur le fil principal - ou y a-t-il une pénalité en n'effectuant pas cette vérification avant d'appeler le code ci-dessous?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});
Egil
la source
117
Cinq ans plus tard, je ne me souviens toujours pas de la syntaxe des blocs GCD et je me retrouve ici à chaque fois.
SpaceTrucker
7
@SpaceTrucker - C'est la même raison pour laquelle je suis sur cette page: D
Matthew Cawley
4
9 ans plus tard, et je viens encore de copier la syntaxe de cette page.
Osa

Réponses:

153

Non, vous n'avez pas besoin de vérifier si vous êtes déjà sur le fil principal. En répartissant le bloc dans la file d'attente principale, vous planifiez simplement le bloc à exécuter en série sur le thread principal, ce qui se produit lorsque la boucle d'exécution correspondante est exécutée.

Si vous êtes déjà sur le thread principal, le comportement est le même: le bloc est planifié et exécuté lorsque la boucle d'exécution du thread principal est exécutée.


la source
3
La question était de savoir s'il y avait une "pénalité en n'effectuant pas cette vérification" ... Je pense qu'il y a une pénalité de performance pour l'utilisation de la répartition asynchrone quand ce n'est pas nécessaire, ou est-ce trivial?
Dan Rosenstark
1
@Yar Je ne pense pas qu'il y ait un impact notable sur les performances dans la plupart des cas: GCD est une bibliothèque légère. Cela dit, j'ai compris la question comme suit: «étant donné le code ci-dessous, dois-je vérifier si je suis sur le thread principal?
7
Cependant, vous DEVEZ vérifier si vous utilisez dispatch_sync. Sinon, vous obtenez une impasse.
Victor Engel
Si vous êtes dans la file d'attente principale et asyncrenvoyé dans la file d'attente principale, il sera exécuté, mais cela peut perturber le calendrier prévu de vos actions . Par exemple, le code de l'interface utilisateur viewDidLoad() ne s'exécute qu'après l'affichage initial de la vue .
pkamb
106

Pour le cas de répartition asynchrone que vous décrivez ci-dessus, vous ne devriez pas avoir besoin de vérifier si vous êtes sur le thread principal. Comme l'indique Bavarious, cela sera simplement mis en file d'attente pour être exécuté sur le thread principal.

Cependant, si vous essayez de faire ce qui précède à l'aide de a dispatch_sync()et que votre rappel est sur le thread principal, votre application se bloquera à ce stade. Je décris cela dans ma réponse ici , car ce comportement m'a surpris lors du déplacement de code -performSelectorOnMainThread:. Comme je le mentionne ici, j'ai créé une fonction d'aide:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

qui exécutera un bloc de manière synchrone sur le thread principal si la méthode dans laquelle vous vous trouvez n'est pas actuellement sur le thread principal, et exécute simplement le bloc en ligne s'il l'est. Vous pouvez utiliser une syntaxe comme celle-ci pour l'utiliser:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
Brad Larson
la source
1
Pourquoi ne pas l'appeler quelque chose comme "runOnMainQueueSync"? Le fait qu'il puisse se bloquer et ne pas le faire est le genre de chose que je ne voudrais pas avoir partout dans mon code. Merci et +1 comme toujours.
Dan Rosenstark,
2
@Yar - Je viens de lui donner ce nom pour que je comprenne bien pourquoi je ne tirais pas simplement des blocs de manière synchrone sur la file d'attente principale au lieu d'utiliser cette fonction d'assistance. C'était plus un rappel pour moi.
Brad Larson
3
Il est toujours possible de bloquer avec cette fonction. Synchronisation de la file d'attente principale> synchronisation des autres files d'attente> la file d'attente principale se bloquera.
hfossli
2
@hfossli - Certes, il ne traitera pas tous les cas. Dans mon utilisation, j'appelais toujours à partir d'une répartition asynchrone sur une file d'attente série en arrière-plan ou du code en ligne sur le thread principal. Je me demande si dispatch_set_specific()cela aiderait dans le cas que vous décrivez: stackoverflow.com/a/12806754/19679 .
Brad Larson
1
[NSThread isMainThread] renvoie souvent OUI et n'est pas considéré comme sûr pour vérifier ce cas dans la programmation GCD. stackoverflow.com/questions/14716334/…
Will Larche
57

Comme les autres réponses mentionnées, dispatch_async du thread principal est très bien.

Cependant, selon votre cas d'utilisation, il existe un effet secondaire que vous pouvez considérer comme un inconvénient: puisque le bloc est planifié dans une file d'attente, il ne s'exécutera pas tant que le contrôle ne reviendra pas à la boucle d'exécution, ce qui aura pour effet de retarder l'exécution de votre bloc.

Par exemple,

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

Imprime:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

Pour cette raison, si vous vous attendiez à ce que le bloc s'exécute entre les NSLog externes, dispatch_async ne vous aiderait pas.

Michael Chinen
la source
Faut dire que c'est une chose importante à considérer
Marc-Alexandre Bérubé
Puisque vous souhaitez vérifier, cela signifie que le code peut ou non s'exécuter dans le thread principal. Déjà dans le thread principal s'exécutera dans cet ordre, sinon cela ne peut pas être garanti. Vous devez donc éviter de concevoir le code pour qu'il s'exécute dans un ordre spécifique lorsque vous vous référez à la programmation multithread. Essayez d'utiliser la méthode de rappel asynchrone.
ooops
1

Non, vous n'avez pas besoin de vérifier si vous êtes dans le fil principal. Voici comment vous pouvez le faire dans Swift:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Il est inclus en tant que fonction standard dans mon référentiel, consultez-le: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
la source