Vérifiez si le thread actuel est le thread principal


121

Existe-t-il un moyen de vérifier si le thread actuel est le thread principal d'Objective-C?

Je veux faire quelque chose comme ça.

  - (void)someMethod
  {
    if (IS_THIS_MAIN_THREAD?) {
      NSLog(@"ok. this is main thread.");
    } else {
      NSLog(@"don't call this method from other thread!");
    }
  }

quel est le problème dans l'appel d'une méthode à partir d'autres threads?
David 天宇 Wong

Réponses:



24

Si vous souhaitez qu'une méthode soit exécutée sur le thread principal, vous pouvez:

- (void)someMethod
{
    dispatch_block_t block = ^{
        // Code for the method goes here
    };

    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

5
Les réponses aux anciennes questions peuvent bénéficier d'une explication de la différence entre la nouvelle réponse et les réponses existantes.
Jason Aller

1
C'est exagéré, si du travail doit être effectué sur le thread principal, il est inutile de vérifier si vous êtes sur le thread principal ou non. Just doNSOperationQueue.mainQueue().addOperationWithBlock { //your work here }
Eric

3
@Eric Je suis d'accord, mais que faire si vous voulez une exécution immédiate de la méthode si déjà dans le thread principal? Dans votre suggestion, la méthode est toujours envoyée pour être exécutée plus tard via la file d'attente des opérations principale.
boherna

@boherna correct, c'est quelque chose à surveiller.
Eric

@boherna Commentaire tardif, mais le point que vous faites dans votre commentaire serait plus fort si vous l'utilisiez à la dispatch_sync()place de dispatch_async()dans votre exemple.
Caleb


13

Si vous voulez savoir si vous êtes ou non sur le thread principal, vous pouvez simplement utiliser le débogueur. Définissez un point d'arrêt sur la ligne qui vous intéresse, et lorsque votre programme l'atteint, appelez ceci:

(lldb) thread info

Cela affichera des informations sur le fil sur lequel vous êtes:

(lldb) thread info thread #1: tid = 0xe8ad0, 0x00000001083515a0 MyApp`MyApp.ViewController.sliderMoved (sender=0x00007fd221486340, self=0x00007fd22161c1a0)(ObjectiveC.UISlider) -> () + 112 at ViewController.swift:20, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1

Si la valeur de queueest com.apple.main-thread, vous êtes sur le thread principal.


6

Le modèle suivant assurera qu'une méthode est exécutée sur le thread principal:

- (void)yourMethod {
    // make sure this runs on the main thread 
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:_cmd/*@selector(yourMethod)*/
                               withObject:nil
                            waitUntilDone:YES];
        return;
    }
    // put your code for yourMethod here
}

_cmdutilisera automatiquement la méthode dans laquelle votre extrait est collé dans ʕ • ᴥ • ʔ
Albert Renshaw

3

Deux manières. D'après la réponse de @ rano,

[[NSThread currentThread] isMainThread] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

Aussi,

[[NSThread mainThread] isEqual:[NSThread currentThread]] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

3
void ensureOnMainQueue(void (^block)(void)) {

    if ([[NSOperationQueue currentQueue] isEqual:[NSOperationQueue mainQueue]]) {

        block();

    } else {

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            block();

        }];

    }

}

notez que je vérifie la file d'attente des opérations, pas le fil, car c'est une approche plus sûre


Cela devrait être la réponse acceptée, fil de discussion principal! = File d'attente principale
railparade

2

Pour Monotouch / Xamarin iOS, vous pouvez effectuer la vérification de cette manière:

if (NSThread.Current.IsMainThread)
{
    DoSomething();
}
else
{
    BeginInvokeOnMainThread(() => DoSomething());
}

1

Version Swift


if (NSThread.isMainThread()) {
    print("Main Thread")
}


0

Détails

  • Swift 5.1, Xcode 11.3.1

Solution 1. Détectez toute file d'attente

Obtenir DispatchQueue actuel?

Solution 2. Détecter uniquement la file d'attente principale

import Foundation

extension DispatchQueue {

    private struct QueueReference { weak var queue: DispatchQueue? }

    private static let key: DispatchSpecificKey<QueueReference> = {
        let key = DispatchSpecificKey<QueueReference>()
        let queue = DispatchQueue.main
        queue.setSpecific(key: key, value: QueueReference(queue: queue))
        return key
    }()

    static var isRunningOnMainQueue: Bool { getSpecific(key: key)?.queue == .main }
}

Usage

if DispatchQueue.isRunningOnMainQueue { ... }

Échantillon

func test(queue: DispatchQueue) {
    queue.async {
        print("--------------------------------------------------------")
        print("queue label: \(queue.label)")
        print("is running on main queue: \(DispatchQueue.isRunningOnMainQueue)")
    }
}

test(queue: DispatchQueue.main)
sleep(1)
test(queue: DispatchQueue.global(qos: .background))
sleep(1)
test(queue: DispatchQueue.global(qos: .unspecified))

Résultat (journal)

--------------------------------------------------------
queue label: com.apple.root.background-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.root.default-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.main-thread
is running on main queue: true

0
Here is a way to detect what the current queue is
extension DispatchQueue {
    //Label of the current dispatch queue.
    static var currentQueueLabel: String { String(cString: __dispatch_queue_get_label(nil)) }

    /// Whether the current queue is a `NSBackgroundActivityScheduler` task.
    static var isCurrentQueueNSBackgroundActivitySchedulerQueue: Bool { currentQueueLabel.hasPrefix("com.apple.xpc.activity.") }

    /// Whether the current queue is a `Main` task.
    static var isCurrentQueueMainQueue: Bool { currentQueueLabel.hasPrefix("com.apple.main-thread") }
}

-2

MISE À JOUR: semble que ce ne soit pas la solution correcte, selon l'en-tête queue.h comme mentionné @demosten

La première pensée m'a été apportée, lorsque j'ai eu besoin de cette fonctionnalité, c'était la ligne:

dispatch_get_main_queue() == dispatch_get_current_queue();

Et avait cherché la solution acceptée:

[NSThread isMainThread];

mine solution 2,5 fois plus rapide.

PS Et oui, j'avais vérifié, ça marche pour tous les threads


3
Cela a du sens - votre méthode contourne la surcharge du système de messagerie d'exécution obj-c. Bien que si vous utilisez cette technique, je dirais qu'elle a une mauvaise odeur de code, peut-être celle d'une optimisation prématurée.
ArtOfWarfare

4
dispatch_get_current_queue () est obsolète depuis iOs 6.0
Durai Amuthan.H

33
Vous pouvez lire ceci dans la description de l'en-tête queue.h d'Apple où dispatch_get_current_queue () est défini: When dispatch_get_current_queue() is called on the main thread, it may or may not return the same value as dispatch_get_main_queue(). Comparing the two is not a valid way to test whether code is executing on the main thread.
demosten
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.