web-dev-qa-db-fra.com

Obtenir la file d'attente de distribution actuelle?

J'ai une méthode qui devrait pouvoir être appelée depuis n'importe quelle file d'attente et à laquelle je m'attendrais. Il exécute du code dans un thread d’arrière-plan, puis utilise dispatch_get_main_queue lorsqu’il renvoie une valeur à son argument de bloc. Je ne veux pas le forcer dans la file d'attente principale s'il ne l'était pas quand il est entré dans la méthode. Est-il possible d’obtenir un pointeur sur la file d’attribution en cours?

40
Andrew

Vous avez la possibilité de "dispatch_get_current_queue()" , cependant, le kit de développement logiciel iOS 6.1 définit cette API avec ces clauses de non-responsabilité:

"Recommended for debugging and logging purposes only:"

et

"This function is deprecated and will be removed in a future release.".

Voici une autre question connexe avec quelques alternatives vous pouvez envisager si vous voulez un code à l'épreuve du temps.

23
Michael Dautermann

Vous pouvez utiliser NSOperationQueue pour cela . NSOperationQueue a la fonction de classe [NSOperationQueue currentQueue], qui renvoie la file d'attente actuelle sous forme d'objet NSOperationQueue. Pour obtenir l'objet de file d'attente de répartition, vous pouvez utiliser [NSOperationQueue currentQueue].underlyingQueue, qui renvoie votre file d'attente actuelle sous forme de dispatch_queue_t.

Swift 3: 

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
}

- fonctionne pour la file d'attente principale!

28
Palle

Avec la désapprobation de dispatch_get_current_queue(), il n’existe aucun moyen de savoir dans quelle file d’exécution vous exécutez. Si vous parcourez les sources GCD , vous constaterez que c'est parce qu'il peut y avoir plusieurs réponses à la question "sur quelle file d'attente suis-je en train de m'exécuter?" (Parce que les files d'attente finissent par cibler l'une des files d'attente globales, etc.)

Si vous voulez garantir qu'un futur bloc est exécuté sur une file d'attente spécifique, le seul moyen est de faire accepter à votre API une file d'attente en tant que paramètre avec le bloc d'achèvement. Cela permet à l'appelant de décider où l'exécution sera exécutée.

Si le simple fait de savoir si l'appelant est sur le thread principal ou non est suffisant, vous pouvez utiliser +[NSThread isMainThread] pour le savoir. Dans le cas habituel, tous les blocs exécutés sur la file d'attente principale GCD seront exécutés sur le thread principal. Une exception à cette règle est que si votre application utilise dispatch_main() à la place d'une boucle d'exécution principale, vous devrez utiliser dispatch_get_specific et amis pour détecter avec certitude que vous vous exécutez dans la file d'attente principale - il s'agit là d'une circonstance relativement rare. Plus généralement, notez que tout le code qui s'exécute sur le thread principal ne s'exécute pas sur la file d'attente principale via GCD; GCD est subordonné au thread runloop principal. Dans votre cas, cela semble être suffisant.

27
ipmcc

Avec la désapprobation de dispatch_get_current_queue(), vous ne pouvez pas obtenir directement un pointeur sur la file d'attente sur laquelle vous travaillez, cependant vous pouvez obtenir le libellé de la file d'attente actuelle} en appelant dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), ce qui vous donne une certaine souplesse. 

Vous pouvez toujours vérifier si vous êtes dans cette file spécifique en comparant simplement leurs étiquettes. Ainsi, si vous ne voulez pas forcer la file d'attente principale, vous pouvez simplement utiliser l'indicateur suivant lorsque vous avez entré la méthode: 

let isOnMainQueue = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))

Si vous travaillez sur la file d'attente globale, vous obtiendrez respectueusement l'étiquette de la file d'attente associée à son type de qualité de service, qui peut être l'un des suivants:

com.Apple.root.user-interactive-qos //qos_class_t(rawValue: 33)
com.Apple.root.user-initiated-qos   //qos_class_t(rawValue: 25)
com.Apple.root.default-qos          //qos_class_t(rawValue: 21)  
com.Apple.root.utility-qos          //qos_class_t(rawValue: 17)
com.Apple.root.background-qos       //qos_class_t(rawValue: 9) 

Et vous pouvez ensuite utiliser dispatch_get_global_queue(qos_class_self(), 0) qui vous redonnera la même file d'attente globale que celle que vous exécutez. 

Mais je pense que Apple nous décourage particulièrement de lier la logique à la file d’attente à laquelle nous avons été appelés, il est donc préférable de l’utiliser à des fins de débogage exclusivement.

14
ambientlight

Il existe encore un moyen de comparer la file d'attente.

Lorsque vous configurez votre file d'attente, veillez à ajouter l'étiquette. Pour mes besoins, j'ai une file d'attente partagée utilisée pour accéder à une base de données afin d'empêcher le verrouillage de la base de données. Dans mon fichier DB.m, j'ai défini la fonction de file d'attente partagée comme suit:

const char *kTransactionQueueLabel = "DB_TRANSACTION_DISPATCH_QUEUE";

+ (dispatch_queue_t)sharedDBTransactionQueue {
    static dispatch_queue_t sharedDBQueue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedDBQueue = dispatch_queue_create(kTransactionQueueLabel, DISPATCH_QUEUE_SERIAL);
    });

    return sharedDBQueue;
}

La file d'attente de transaction de base de données partagée est utilisée localement dans le fichier pour répartir toutes les exécutions dans la base de données. Cependant, il existe également un accesseur public sur ce point pour permettre l'envoi de transactions entières dans la base de données. Ainsi, en interne, si une méthode d'accès à la base de données est appelée depuis la file d'attente des transactions, nous devons effectuer une distribution interne sur une file d'attente différente (toutes les distributions synchrones). Donc, en interne, j’envoie toujours dans la file d’attente appropriée en utilisant le getter ci-dessous.

/**
 * @description Decide which queue to use - if we are already in a transaction, use the internal access queue, otherwise use the shared transaction queue.
 */
- (dispatch_queue_t)getProperQueueForExecution {
    const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
    dispatch_queue_t sharedAccessQueue = [DB sharedDBTransactionQueue];
    if (strcmp(currentLabel, kTransactionQueueLabel) == 0) {
        sharedAccessQueue = [DB sharedInternalDBAccessQueue];
    }

    return sharedAccessQueue;
}

Espérons que cela aide. Désolé pour le long exemple. L’essentiel est que vous pouvez utiliser

const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);

pour obtenir l'étiquette de la file actuelle et la comparer à une étiquette définie.

2
johnrechd

En tant qu'approche alternative à cette méthode de NSOBjectperformSelector: withObject: afterDelay: distribue l'appel sur la boucle d'exécution du thread en cours. Selon les docs:

Cette méthode configure une minuterie pour exécuter le message aSelector sur le fichier la boucle d'exécution du thread actuel.

Évidemment, je suggère d'utiliser ceci avec un délai de zéro, ce qui, selon la documentation à nouveau:

Spécifier un délai de 0 ne signifie pas nécessairement que le sélecteur est effectuée immédiatement. Le sélecteur est toujours en file d'attente sur le thread exécuter en boucle et effectué dès que possible.

Malheureusement, il nécessite exactement un argument. Par conséquent, certaines solutions de contournement peuvent être nécessaires si votre méthode prend plus ou moins.

Une autre chose que j'ai notée est que cette méthode n'est pas disponible pour les protocoles, mais uniquement pour les implémentations. Cela est dû au fait que cette méthode réside dans une catégorie NSObject et non dans l'interface NSObject (voir PS ci-dessous). Cela peut facilement être corrigé en convertissant en id.

PS: Il existe deux NSObjects différents, un protocole et une implémentation. Remarquez la déclaration NSObject:

@interface NSObject <NSObject> { ... }

Cela peut sembler étrange, mais l’un est en cours de déclaration (après @interface) et l’autre est un protocole précédemment déclaré (entre < et >). Lors de la déclaration d'un protocole qui étend NSObject (c'est-à-dire, @protocol Foo <NSObject>), le protocole hérite des méthodes les plus récentes, mais pas les plus anciennes. Finalement, le protocole est implémenté par une classe héritant de l'implémentation NSObject, de sorte que toutes les instances héritant de l'implémentation NSObject sont toujours valables. Mais je m'éloigne du sujet.

1
André Fratelli

J'ai les mêmes exigences fonctionnelles que le post original mentionne. Vous devriez pouvoir appeler cette fonction asynchrone sur n'importe quelle file d'attente, mais si elle est appelée sur la file principale, rappelez l'utilisateur à la file d'attente principale. Je me contente de le gérer comme suit:

// cache value for if we should callback on main queue
BOOL callbackOnMT = [NSThread isMainThread];

// ...
// ... do async work...
// ...

if (callbackOnMT && ![NSThread isMainThread]){
    dispatch_async(dispatch_get_main_queue(), ^{
        // callback to user on main queue
        // as they called this function on main queue
        callbackToUser();
    });
}
else{
    // callback to user on our current queue
    // as they called this function on a non-main queue
    callbackToUser();
}
0
user1687195