2013-02-04 15 views
5

Ho una classe di caricamento immagine che fornisce carichi NSURL e immagine dal web ed esegue il blocco di completamento. Il codice è in realtà abbastanza sempliceProblema con GCD e troppi thread

- (void)downloadImageWithURL:(NSString *)URLString completion:(BELoadImageCompletionBlock)completion 
{ 
    dispatch_async(_queue, ^{ 
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
     UIImage *image = nil; 
     NSURL *URL = [NSURL URLWithString:URLString]; 
     if (URL) { 
      image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]]; 
     } 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      completion(image, URLString); 
     }); 
    }); 

}

Se si sostituisce

dispatch_async(_queue, ^{ 

con commentate

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 

Immagini stanno caricando molto più veloce, wich è abbastanza logico (prima che le immagini verrebbero caricate una alla volta, ora un mucchio di queste si caricano contemporaneamente). Il mio problema è che ho forse 50 immagini e chiamo downloadImageWithURL: completion: metodo per tutti e quando uso la coda globale invece di _queue la mia app alla fine si blocca e vedo che ci sono oltre 85 thread. Il problema può essere che la mia chiamata dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0) 50 volte di seguito fa sì che GCD crei troppi thread? Ho pensato che Gcd gestisca tutto il calpestio e si assicuri che il numero di thread non sia enorme, ma se non è il caso, posso influenzare il numero di thread?

risposta

2

Bene da http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

code simultanee (noto anche come un tipo della coda di invio globale) esegue contemporaneamente una o più attività, ma le attività sono ancora avviate in nell'ordine in cui sono state aggiunte ed in coda. Le attività di esecuzione attualmente vengono eseguite su thread distinti gestiti dalla coda di invio . Il numero esatto di attività eseguite in un dato punto è variabile e dipende dalle condizioni del sistema.

e

code seriali (noto anche come code di spedizione privati) eseguire un compito alla volta nell'ordine in cui vengono aggiunti alla coda. L'attività attualmente in esecuzione viene eseguita su un thread distinto (che può variare da da un'attività a un'attività) gestita dalla coda di invio.

con l'invio di tutti i blocchi sulla coda di invio concomitante alta priorità con

[NSData dataWithContentsOfURL:URL] 

che è un'operazione rete blocco sincrono, sembra che il comportamento GCD predefinito sarà a generare un carico di thread eseguire i blocchi al più presto.

È necessario essere spediti allo DISPATCH_QUEUE_PRIORITY_BACKGROUND. Queste attività non sono in alcun modo "ad alta priorità". Qualsiasi elaborazione delle immagini dovrebbe essere eseguita quando c'è tempo libero e non accade nulla sul thread principale.

Se si desidera un maggiore controllo su quante di queste cose stanno accadendo in una volta, consiglio di utilizzare NSOperation. Puoi prendere i blocchi e incorporarli in un'operazione utilizzando NSBlockOperation e quindi puoi inviare queste operazioni al tuo NSOperationQueue. Un NSOperationQueue ha un - (NSInteger)maxConcurrentOperationCount e come un vantaggio aggiunto le operazioni possono anche essere annullate dopo la pianificazione, se necessario.

+4

No, questo non ha nulla a che fare con la priorità della coda. GCD crea solo thread aggiuntivi quando i blocchi su thread esistenti per un blocco di code simultanee globali nel kernel per un periodo di tempo non banale. – das

+0

@das grazie Ho apportato alcune modifiche ma non esitare a rendere la mia risposta ancora migliore :) – jackslash

1

È possibile utilizzare NSOperationqueue, che è supportato da NSURLConnection

Ed ha il seguente metodo di istanza:

- (void)setMaxConcurrentOperationCount:(NSInteger)count 
8

Il kernel crea thread aggiuntivi quando le unità di lavoro sui thread di lavoro GCD esistenti per una coda concorrente globale vengono bloccate nel kernel per un periodo di tempo significativo (purché ci sia ulteriore lavoro in sospeso sulla coda globale).

Ciò è necessario affinché l'applicazione possa continuare a progredire globalmente (ad esempio l'esecuzione di uno dei blocchi in sospeso potrebbe essere ciò che consente di sbloccare i thread bloccati).

Se il motivo per i thread di lavoro da bloccare nel kernel è IO (ad esempio lo +[NSData dataWithContentsOfURL:] in questo esempio), la soluzione migliore è sostituire quelle chiamate con un'API che eseguirà quell'IO in modo asincrono senza bloccare, ad es. NSURLConnection per l'I/O di rete o di invio per il filesystem IO.

In alternativa è possibile limitare manualmente il numero di operazioni di blocco simultanee, ad es. utilizzando un semaforo di invio di conteggio.

La sessione GCD WWDC 2012 ha approfondito questo argomento in dettaglio.