Comprensione dispatch_async


233

Ho delle domande su questo codice

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Il primo parametro di questo codice è

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Stiamo chiedendo a questo codice di eseguire attività seriali su una coda globale la cui stessa definizione è che restituisce una coda concorrente globale di un determinato livello di priorità?

Qual è il vantaggio dell'utilizzo dispatch_get_global_queuesulla coda principale?

Sono confuso. La prego di aiutarmi a capirlo meglio.


1
Dovresti tagliare meglio il tuo codice in più righe in modo che abbia più senso. al sicuro dispatch_get_global_queuedentro di te un tipo variabile di dispatch_queue_t myQueue. È più leggibile passare solo myQueue al tuo `` dispatch_async`
Alex Cio,

Risposte:


517

Il motivo principale per cui si utilizza la coda predefinita sulla coda principale è eseguire attività in background.

Ad esempio, se sto scaricando un file da Internet e voglio aggiornare l'utente sull'avanzamento del download, eseguirò il download nella coda predefinita prioritaria e aggiornerò l'interfaccia utente nella coda principale in modo asincrono.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

Capisco che David ringrazia per la sua risposta, ma la mia domanda era più in giro per capire la logica di farlo, cioè
sto

Sto facendo esattamente quello che mi suggerisci, ma in qualche modo, uiTableViewCell non si aggiorna immediatamente quando chiamo [self.tableView reloadData] negli Aggiornamenti dell'interfaccia utente. Ci vogliono circa 4 o 5 secondi. Mi sta facendo impazzire da diversi giorni ormai .
GrandSteph

@GrandSteph Non ho molta familiarità con quel metodo. Forse quel metodo richiede solo 5 secondi per essere eseguito. La cosa importante con dispatch_async è che ti permette di fare cose in background senza appendere il thread principale.
David,

2
cosa 0significa?
Miele

3
@Honey Lo 0 è il flagsparametro, che attualmente non fa assolutamente nulla. Dai documenti:Flags that are reserved for future use. Always specify 0 for this parameter.
David,

199

Tutte le code DISPATCH_QUEUE_PRIORITY_X sono code simultanee (nel senso che possono eseguire più attività contemporaneamente) e sono FIFO nel senso che le attività all'interno di una determinata coda inizieranno a essere eseguite usando l'ordine "first in, first out". Questo è rispetto alla coda principale (da dispatch_get_main_queue ()), che è una coda seriale (le attività inizieranno l'esecuzione e finiranno l'esecuzione nell'ordine in cui vengono ricevute).

Pertanto, se si inviano 1000 blocchi dispatch_async () a DISPATCH_QUEUE_PRIORITY_DEFAULT, tali attività inizieranno a essere eseguite nell'ordine in cui sono state inviate nella coda. Allo stesso modo per le code HIGH, LOW e BACKGROUND. Tutto ciò che invii in una di queste code viene eseguito in background su thread alternativi, lontano dal thread dell'applicazione principale. Pertanto, queste code sono adatte per eseguire attività come download in background, compressione, calcolo, ecc.

Si noti che l'ordine di esecuzione è FIFO in base alla coda. Quindi, se invii 1000 attività dispatch_async () alle quattro diverse code simultanee, suddividendole uniformemente e inviandole a BACKGROUND, LOW, DEFAULT e HIGH in ordine (cioè pianifichi le ultime 250 attività sulla coda HIGH), è molto probabile che le prime attività che vedi avviarsi saranno su quella coda ALTA poiché il sistema ha preso la tua implicazione che tali attività devono arrivare alla CPU il più rapidamente possibile.

Nota anche che dico "inizierà l'esecuzione in ordine", ma tieni presente che, come code simultanee, le cose non finiranno necessariamente in ordine di esecuzione in base al periodo di tempo per ogni attività.

Secondo Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Una coda di invio simultanea è utile quando si hanno più attività che possono essere eseguite in parallelo. Una coda concorrente è ancora una coda in quanto consente di accodare le attività in un ordine first-in, first-out; tuttavia, una coda concorrente può rimuovere le attività aggiuntive prima del completamento di qualsiasi attività precedente. Il numero effettivo di attività eseguite da una coda simultanea in un dato momento è variabile e può cambiare in modo dinamico al variare delle condizioni dell'applicazione. Numerosi fattori influenzano il numero di attività eseguite dalle code simultanee, incluso il numero di core disponibili, la quantità di lavoro svolto da altri processi e il numero e la priorità delle attività in altre code di invio seriale.

Fondamentalmente, se invii quei 1000 blocchi dispatch_async () a una coda DEFAULT, HIGH, LOW o BACKGROUND, tutti inizieranno l'esecuzione nell'ordine in cui li invii. Tuttavia, le attività più brevi possono terminare prima di quelle più lunghe. I motivi alla base di ciò sono se ci sono core di CPU disponibili o se le attività di coda correnti eseguono un lavoro non intensivo dal punto di vista computazionale (facendo così pensare al sistema di poter inviare attività aggiuntive in parallelo indipendentemente dal numero di core).

Il livello di concorrenza è gestito interamente dal sistema e si basa sul carico del sistema e su altri fattori determinati internamente. Questa è la bellezza di Grand Central Dispatch (il sistema dispatch_async ()): basta creare le unità di lavoro come blocchi di codice, impostare una priorità per esse (in base alla coda scelta) e lasciare che il sistema gestisca il resto.

Quindi, per rispondere alla domanda sopra: sei parzialmente corretto. Stai "chiedendo quel codice" per eseguire attività simultanee su una coda simultanea globale al livello di priorità specificato. Il codice nel blocco verrà eseguito in background e qualsiasi codice aggiuntivo (simile) verrà eseguito potenzialmente in parallelo a seconda della valutazione del sistema delle risorse disponibili.

La coda "principale" invece (da dispatch_get_main_queue ()) è una coda seriale (non concorrente). Le attività inviate alla coda principale verranno sempre eseguite in ordine e finiranno sempre in ordine. Queste attività verranno eseguite anche sul thread dell'interfaccia utente, quindi è adatto per aggiornare l'interfaccia utente con messaggi di avanzamento, notifiche di completamento, ecc.


+1, ma penso che in pratica non importa molto se le code simultanee sono FIFO o solo un ordine casuale. Se avvii 5 attività in un ciclo, supponi che essenzialmente inizieranno contemporaneamente. Non esiste alcuna garanzia che, ad esempio, la prima operazione di I / O del 1o task si verifichi prima del 5o, anche se eseguono lo stesso codice. OTOH, per le code seriali, il comportamento FIFO è essenziale e IMHO questa è la differenza decisiva tra i due tipi di coda.
Gerhard Wesp,

Spiegazione incredibile. Clap molto!
Okhan Okbay,

36

Versione rapida

Questa è la versione rapida della risposta Objective-C di David. Si utilizza la coda globale per eseguire le cose in background e la coda principale per aggiornare l'interfaccia utente.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
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.