Differenza tra DispatchQueue.main.async e DispatchQueue.main.sync


99

Uso DispatchQueue.main.asyncda molto tempo per eseguire operazioni relative all'interfaccia utente.



Swift fornisce sia DispatchQueue.main.asynce DispatchQueue.main.sync, sia vengono eseguiti sulla coda principale.



Qualcuno può dirmi la differenza tra loro? Quando dovrei usarli?



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}

Risposte:


46

Quando lo si utilizza async, la coda delle chiamate si sposta senza attendere finché il blocco inviato non viene eseguito. Al contrario sync, la coda delle chiamate si fermerà e attenderà fino a quando il lavoro che hai inviato nel blocco sarà completato. Pertanto syncè soggetto a portare a deadlock. Prova a correre DispatchQueue.main.syncdalla coda principale e l'app si bloccherà perché la coda di chiamata aspetterà fino alla fine del blocco inviato ma non sarà nemmeno in grado di avviarsi (perché la coda è ferma e in attesa)

Quando usarlo sync? Quando è necessario attendere che qualcosa venga fatto su una coda DIVERSA e solo allora continuare a lavorare sulla coda corrente

Esempio di utilizzo della sincronizzazione:

Su una coda seriale potresti usare synccome mutex per assicurarti che un solo thread sia in grado di eseguire il pezzo di codice protetto allo stesso tempo.


Sarebbe sbagliato chiamare DispatchQueue.main.syncda un thread in background?
Honey

@Honey In generale no, non c'è niente di sbagliato in una chiamata del genere (purché la coda principale non faccia nulla di pesante e disponga di tempo), ma in pratica non riesco a pensare a una situazione in cui ne hai davvero bisogno. Ci dovrebbe essere sicuramente una soluzione migliore
Andrey Chernukha

1
@Honey Una di queste situazioni è l'aggiornamento di una CollectionView di PHAssets dall'API PhotoKit, come dimostrato nella documentazione qui: developer.apple.com/documentation/photokit/…
teacup

1
@teacup interessante. Mi chiedo solo come sarebbe diverso se chiamassimo asynclì? Voglio dire, poiché non c'è nient'altro sul thread in seguito, non fa differenza. Se fosse stato DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};allora avrebbe avuto senso. Ma quando non ci sono altri blocchi, non riesco a pensare ai vantaggi dell'utilizzo di DispatchQueue.main.sync {Oneblock}over DispatchQueue.main.async {Oneblock}. Per entrambi avranno la priorità / immediatezza mainQueue e nulla li interromperà.
Miele

3
@Honey "poiché non c'è nient'altro sul thread in seguito" non è vero quando sei nel thread principale, che è responsabile di gestire tutte le interazioni dell'utente con l'app. Quindi, ad esempio, un utente potrebbe eliminare un'altra foto prima che photoLibraryDidChange ritorni con un'origine dati aggiornata causando un errore irreversibile di incoerenza.
tazza da tè

160

Perché la concorrenza?

Non appena aggiungi attività pesanti alla tua app come il caricamento dei dati, rallenta il lavoro dell'interfaccia utente o addirittura la blocca. La concorrenza consente di eseguire 2 o più attività "contemporaneamente". Lo svantaggio di questo approccio è che la sicurezza del thread non è sempre facile da controllare. Senti quando attività diverse vogliono accedere alle stesse risorse come provare a cambiare la stessa variabile su thread diversi o accedere alle risorse già bloccate dai diversi thread.

Ci sono alcune astrazioni di cui dobbiamo essere consapevoli.

  • Code.
  • Prestazioni delle attività sincrone / asincrone.
  • Priorità.
  • Problemi comuni.

Code

Deve essere seriale o simultaneo . Oltre che globale o privato allo stesso tempo.

Con le code seriali, le attività verranno completate una per una mentre con le code simultanee, le attività verranno eseguite simultaneamente e saranno terminate secondo pianificazioni impreviste. Lo stesso gruppo di attività richiederà più tempo su una coda seriale rispetto a una coda simultanea.

Puoi creare le tue code private (sia seriali che simultanee ) o utilizzare le code globali (di sistema) già disponibili . La coda principale è l'unica coda seriale tra tutte le code globali .

Si consiglia vivamente di non eseguire attività pesanti che non sono riferite al lavoro dell'interfaccia utente sulla coda principale (ad esempio il caricamento dei dati dalla rete), ma invece di eseguirle sulle altre code per mantenere l'interfaccia utente non congelata e reattiva alle azioni dell'utente. Se lasciamo che l'interfaccia utente venga modificata sulle altre code, le modifiche possono essere apportate a una pianificazione e velocità diverse e inaspettate. Alcuni elementi dell'interfaccia utente possono essere disegnati prima o dopo che sono necessari. Può mandare in crash l'interfaccia utente. Dobbiamo anche tenere presente che poiché le code globali sono code di sistema, ci sono alcune altre attività che possono essere eseguite dal sistema su di esse.

Qualità del servizio / Priorità

Le code hanno anche diversi qos (Quality of Service) che imposta la priorità di esecuzione
dell'attività (dalla più alta alla più bassa qui): .userInteractive - coda principale
.userInitiated - per le attività avviate dall'utente su cui l'utente attende una risposta
.utility - per le attività che richiede un po 'di tempo e non richiede una risposta immediata, ad esempio lavorare con i dati
.background - per le attività che non sono correlate alla parte visiva e che non sono rigide per il tempo di completamento).

C'è anche

una coda .default che non trasferisce le informazioni di qos . Se non è stato possibile rilevare il qos, il file qosverrà utilizzato tra .userInitiated e .utility .

Le attività possono essere eseguite in modo sincrono o asincrono .

  • La funzione sincrona restituisce il controllo alla coda corrente solo al termine dell'attività. Blocca la coda e attende fino al termine dell'attività.

  • La funzione asincrona restituisce il controllo alla coda corrente subito dopo che l'attività è stata inviata per essere eseguita sulla coda diversa. Non attende che l'attività sia terminata. Non blocca la coda.

Problemi comuni.

Gli errori più popolari commessi dai programmatori durante la proiezione delle app simultanee sono i seguenti:

  • Condizione di gara : causata quando il funzionamento dell'app dipende dall'ordine di esecuzione delle parti di codice.
  • Inversione di priorità : quando le attività con priorità più alta attendono il completamento delle attività con priorità più bassa a causa del blocco di alcune risorse
  • Deadlock : quando alcune code hanno un'attesa infinita per le origini (variabili, dati ecc.) Già bloccate da alcune di queste code.

Non chiamare MAI la funzione di sincronizzazione sulla coda principale .
Se chiami la funzione di sincronizzazione sulla coda principale, questa bloccherà la coda così come la coda sarà in attesa del completamento dell'attività ma l'attività non sarà mai terminata poiché non sarà nemmeno in grado di avviarsi perché la coda è già bloccato. Si chiama deadlock .

Quando utilizzare la sincronizzazione? Quando è necessario attendere fino al termine dell'attività. Sentite quando ci assicuriamo che qualche funzione / metodo non venga chiamato due volte. Sentiamo che abbiamo la sincronizzazione e cerchiamo di impedire che venga chiamato due volte fino a quando non è completamente finito. Ecco un po 'di codice per questa preoccupazione:
come scoprire cosa ha causato il rapporto di arresto anomalo dell'errore sul dispositivo IOS?


3
Non penso che "NON chiamare MAI la funzione di sincronizzazione sulla coda principale" sia giusto. Ci sono casi in cui si chiama la sincronizzazione nel thread principale, ad esempio, quando si dispone di un contatore globale per cui è necessario che ogni oggetto utilizzi e si aumenta il: dispatchQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold,

6
Classe QOS - .userInteractive NON è la coda principale.
Kunal Shah

1
Sarebbe sbagliato chiamare DispatchQueue.main.syncda un thread in background?
Honey

1
@ Tesoro, no, non è sbagliato chiamarlo, ma dalla mia esperienza, ti ritroveresti a chiamare più DispatchQueue.main.async oltre a sync.
James Kim

2
Non sarebbe più preciso dire che non dovresti mai chiamare la funzione sync () sulla coda corrente? Non è sbagliato chiamare sync () sulla coda principale se sei in un'altra coda, se ho capito bene.
ykay

0

synco asyncmetodi non hanno alcun effetto sulla coda su cui vengono chiamati.

syncbloccherà il thread da cui viene chiamato e non la coda su cui viene chiamato. È la proprietà di DispatchQueuecui decide se il fileDispatchQueue attendere l'esecuzione dell'attività (coda seriale) o può eseguire l'attività successiva prima che l'attività corrente venga completata (coda simultanea).

Quindi, anche quando DispatchQueue.main.asyncè una chiamata asincrona, un'operazione pesante aggiunta può bloccare l'interfaccia utente poiché le sue operazioni vengono eseguite in serie sul thread principale. Se questo metodo viene chiamato dal thread in background, il controllo tornerà a quel thread istantaneamente anche quando l'interfaccia utente sembra essere bloccata. Questo perché la asyncchiamata viene effettuataDispatchQueue.main


0

GCDti consente di eseguire un'attività synchronouslyo asynchronously[Informazioni] [Altro]

synchronousLa funzione (blocca e attendi) restituisce un controllo quando l'attività sarà completata

asynchronousLa funzione (invia e procedi) restituisce immediatamente un controllo, inviando l'attività da avviare a una coda appropriata ma senza attendere il completamento.

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.