GCD per eseguire attività nel thread principale


254

Ho un callback che potrebbe provenire da qualsiasi thread. Quando ricevo questo callback, vorrei eseguire una determinata attività sul thread principale.

Devo verificare se sono già sul thread principale - o c'è qualche penalità non eseguendo questo controllo prima di chiamare il codice qui sotto?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

116
Cinque anni dopo non ricordo ancora la sintassi dei blocchi GCD e finisco qui ogni volta.
SpaceTrucker,

7
@SpaceTrucker - Questo è lo stesso motivo per cui sono in questa pagina: D
Matthew Cawley,

4
9 anni dopo, e vengo ancora a copiare la sintassi da questa pagina.
Osa

Risposte:


152

No, non è necessario verificare se si è già nel thread principale. Inviando il blocco alla coda principale, si sta semplicemente programmando che il blocco venga eseguito in serie sul thread principale, cosa che accade quando viene eseguito il ciclo di esecuzione corrispondente.

Se sei già sul thread principale, il comportamento è lo stesso: il blocco è programmato ed eseguito quando viene eseguito il ciclo di esecuzione del thread principale.


3
La domanda era se ci fosse una "penalità non eseguendo questo controllo" ... Penserei che ci sia una penalità di prestazione per l'uso della spedizione asincrona quando non è necessario o è banale?
Dan Rosenstark,

1
@Yar Non credo che nella maggior parte dei casi vi sia un notevole impatto sulle prestazioni: GCD è una libreria leggera. Detto questo, ho capito la domanda come: "dato il codice qui sotto, devo verificare se sono sul thread principale?"

7
Tuttavia, è necessario verificare se si utilizza dispatch_sync. Altrimenti si ottiene un punto morto.
Victor Engel,

Se sei nella coda principale e rispedisci asyncnella coda principale, verrà eseguito ma ciò potrebbe incasinare i tempi previsti delle tue azioni . Ad esempio il codice dell'interfaccia utente viewDidLoad() non viene eseguito fino a quando la vista non viene visualizzata per la prima volta .
pkamb,

106

Per il caso di spedizione asincrono descritto sopra, non dovresti controllare se sei nel thread principale. Come Bavarous indica, questo sarà semplicemente messo in coda per essere eseguito sul thread principale.

Tuttavia, se si tenta di eseguire quanto sopra utilizzando a dispatch_sync()e il callback si trova sul thread principale, l'applicazione si bloccherà a quel punto. Lo descrivo nella mia risposta qui , perché questo comportamento mi ha sorpreso quando ho spostato del codice da -performSelectorOnMainThread:. Come ho già detto, ho creato una funzione di supporto:

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

che eseguirà un blocco in modo sincrono sul thread principale se il metodo in cui ti trovi non è attualmente sul thread principale e, se lo è, esegue il blocco in linea. È possibile utilizzare la sintassi come la seguente per utilizzare questo:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
Perché non chiamarlo in qualche modo "runOnMainQueueSync"? Il fatto che potrebbe bloccarsi e non è il tipo di cosa che non vorrei avere su tutto il mio codice. Grazie e +1 come sempre.
Dan Rosenstark,

2
@Yar - Ho appena dato quel nome in modo che mi fosse chiaro perché non stavo semplicemente sparando i blocchi in modo sincrono sulla coda principale invece di usare questa funzione di supporto. Era più un promemoria per me stesso.
Brad Larson

3
È ancora possibile eseguire il deadlock con questa funzione. Sincronizzazione coda principale> sincronizzazione altra coda> la coda principale si bloccherà.
hfossli,

2
@hfossli - Vero, non gestirà tutti i casi. Nel mio utilizzo, chiamavo sempre da una spedizione asincrona su una coda seriale in background o un codice inline sul thread principale. Mi chiedo se dispatch_set_specific()sarebbe di aiuto nel caso che descrivi: stackoverflow.com/a/12806754/19679 .
Brad Larson

1
[NSThread isMainThread] restituisce spesso SÌ e non è considerato sicuro per la verifica di questo caso nella programmazione GCD. stackoverflow.com/questions/14716334/…
Will Larche,

57

Come menzionato nelle altre risposte, dispatch_async dal thread principale va bene.

Tuttavia, a seconda del caso d'uso, c'è un effetto collaterale che potresti considerare uno svantaggio: poiché il blocco è programmato su una coda, non verrà eseguito fino a quando il controllo non tornerà al ciclo di esecuzione, che avrà l'effetto di ritardare l'esecuzione del tuo blocco.

Per esempio,

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

Stampa:

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

Per questo motivo, se ti aspettavi che il blocco venisse eseguito tra i NSLog esterni, dispatch_async non ti sarebbe d'aiuto.


Deve dire che questa è una cosa importante da considerare
Marc-Alexandre Bérubé il

Dal momento che si desidera verificare, ciò significa che il codice può o non può essere eseguito nel thread principale. Già nel thread principale verrà eseguito in questo ordine, altrimenti questo non può essere garantito. Quindi dovresti evitare di progettare il codice da eseguire nell'ordine specifico quando fai riferimento alla programmazione multithread. Prova a utilizzare il modo di richiamata asincrono.
ooops

1

No, non è necessario verificare se si è nel thread principale. Ecco come puoi farlo in Swift:

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

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

È incluso come funzione standard nel mio repository, provalo: https://github.com/goktugyil/EZSwiftExtensions

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.