Controlla se il thread corrente è o meno il thread principale


121

C'è un modo per verificare se il thread corrente è o meno il thread principale in Objective-C?

Voglio fare qualcosa di simile.

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

cosa c'è di sbagliato nel chiamare un metodo da altri thread?
David 天宇 Wong

Risposte:



24

Se vuoi che un metodo venga eseguito sul thread principale, puoi:

- (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
Le risposte a vecchie domande possono trarre vantaggio da una spiegazione di come la nuova risposta differisce dalle risposte esistenti.
Jason Aller

1
Questo è eccessivo, se è necessario fare del lavoro sul thread principale, non serve controllare se sei sul thread principale o meno. Basta fareNSOperationQueue.mainQueue().addOperationWithBlock { //your work here }
Eric

3
@Eric sono d'accordo, ma cosa succede se si desidera l'esecuzione immediata del metodo se già nel thread principale? Secondo il tuo suggerimento, il metodo viene sempre inviato per essere eseguito successivamente tramite la coda delle operazioni principali.
boherna

@boherna corretto, è qualcosa a cui prestare attenzione.
Eric,

@boherna Commento in ritardo, ma il punto che fai nel tuo commento sarebbe più forte se lo usassi al dispatch_sync()posto del dispatch_async()tuo esempio.
Caleb


13

Se vuoi sapere se sei o meno sul thread principale, puoi semplicemente usare il debugger. Imposta un punto di interruzione sulla riga che ti interessa e quando il tuo programma lo raggiunge, chiama questo:

(lldb) thread info

Questo mostrerà le informazioni sul thread in cui ti trovi:

(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

Se il valore di queueè com.apple.main-thread, allora sei nel thread principale.


6

Il seguente schema assicurerà che un metodo venga eseguito sul thread principale:

- (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
}

_cmdutilizzerà automaticamente il metodo in cui viene incollato lo snippet in ʕ • ᴥ • ʔ
Albert Renshaw

3

Due strade. Dalla risposta di @ rano,

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

Anche,

[[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();

        }];

    }

}

nota che controllo la coda delle operazioni, non il thread, poiché questo è un approccio più sicuro


Questa dovrebbe essere la risposta accettata, thread principale! = Coda principale
railwayparade

2

Per Monotouch / Xamarin iOS puoi eseguire il controllo in questo modo:

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

1

Versione rapida


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


0

Dettagli

  • Swift 5.1, Xcode 11.3.1

Soluzione 1. Rileva qualsiasi coda

Ricevi DispatchQueue corrente?

Soluzione 2. Rileva solo la coda 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 }
}

uso

if DispatchQueue.isRunningOnMainQueue { ... }

Campione

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))

Risultato (registro)

--------------------------------------------------------
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

AGGIORNAMENTO: sembra che non sia la soluzione corretta, secondo l'intestazione queue.h come menzionato @demosten

Il primo pensiero mi è stato portato, quando mi serviva questa funzionalità era la linea:

dispatch_get_main_queue() == dispatch_get_current_queue();

E aveva cercato la soluzione accettata:

[NSThread isMainThread];

la mia soluzione 2,5 volte più veloce.

PS E sì, avevo controllato, funziona per tutti i thread


3
Ha senso: il tuo metodo aggira il sovraccarico del sistema di messaggistica runtime obj-c. Anche se stai usando questa tecnica, direi che ha un cattivo odore di codice, forse quello di un'ottimizzazione prematura.
ArtOfWarfare

4
dispatch_get_current_queue () è deprecato da iOs 6.0
Durai Amuthan.H

33
Puoi leggere questo nella descrizione dell'intestazione queue.h di Apple dove è definito dispatch_get_current_queue (): 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.