2012-07-15 22 views
6

Sto sviluppando un'applicazione per iPad e attualmente sto cercando di trovare il miglior approccio al multithreading. Consentitemi di illustrare questo con un esempio semplificato:
Ho una vista con 2 sottoview, un raccoglitore di directory e una galleria con le anteprime di tutte le immagini nella directory selezionata. Poiché il "download" e la generazione di queste miniature possono richiedere un po 'di tempo, ho bisogno del multithreading in modo che l'interazione e l'aggiornamento della vista non vengano bloccati.Il migliore approccio multithreading nell'obiettivo C?

Questo è ciò che ho già provato:
[auto performSelectorInBackground: @selector (displayThumbnails :) withObject: CurrentFolder];
Questo ha funzionato correttamente perché le interazioni dell'utente non sono state bloccate, tuttavia fallisce miseramente quando l'utente tocca un'altra cartella mentre la prima cartella è ancora in fase di caricamento. Due thread stanno tentando di accedere alla stessa vista e alle stesse variabili che si traducono in problemi di esecuzione reciproca. Quando gli utenti toccano un'altra cartella, lo displayThumbnails della cartella attualmente in caricamento dovrebbe essere interrotto. Non ho trovato un modo per fare questo ..

NSThreads
ho provato questo, ma ha lottato con quasi gli stessi problemi con il primo metodo, non ho trovato un modo (facile) per annullare la metodo in corso. (Sì, so di [aThread cancel] ma non ho trovato un modo per 'riprendere' il thread). Forse dovrei sottoclasse NSThread e implementare i miei metodi isRunning etc? Ma non c'è un modo migliore o una terza (o addirittura la quarta e quinta) opzione che sto trascurando?

Penso che questo sia un esempio abbastanza semplice e penso che ci sia forse una soluzione migliore senza sottoclasse NSThread. Quindi, cosa faresti? Le vostre opinioni per favore!

+4

Apparentemente non riesco a dare una risposta che legga solo "GCD" – JustSid

+4

Si sbaglia completamente. Sul serio. – puzzle

+0

Vedere [here] (https://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html%23//apple_ref/doc/uid/TP40008091-CH102-SW2) perché GCD è così bello :) O ancora meglio, guarda una delle sessioni WWDC :) Informazioni sul tuo problema originale: Esistono vari modi per annullare o mettere in pausa, indipendentemente dall'utilizzo di thread, GCD o NSOperationQueue. Puoi dormire, sospendere, attendere un blocco, annullare completamente, ecc. Penso che NSOperationQueue sarà una buona soluzione per te in questo momento perché ti guida automaticamente verso un'implementazione ragionevole. – puzzle

risposta

6

NSOperationQueue dovrebbe funzionare bene per questo compito.

Un'altra opzione sarebbe semplice GCD, tuttavia, se non hai mai lavorato con esso, NSOperationQueue è probabilmente la scelta migliore in quanto praticamente ti guida automaticamente a implementare le cose "nel modo giusto", ha modi ovvi per la cancellazione, ecc.

5

Si desidera utilizzare NSOperations simultanee per scaricare ed elaborare le immagini in background. Questi sarebbero gestiti da un NSOperationsQueue. Essenzialmente queste operazioni dovrebbero essere configurate per recuperare un'immagine per operazione, elaborarla, salvarla nel file system, quindi inviare messaggi all'app principale nel thread principale che l'immagine è disponibile.

Ci sono diversi progetti su github che puoi vedere in questo show come fare questo - basta cercare in github usando "Concurrent" o "NSOperation".

iOS ha una funzione davvero piacevole per fare il lavoro in background. Grand Central Dispatch (GCD) e Blocks, ma questi non ti permettono di avere un oggetto usando i callback delegati - quindi NSOperation.

Quindi è necessario leggere su blocchi, GCD, e quindi guardare qualche codice di NSOperations concomitante open source. L'uso di NSOperations simultanee non è semplice come utilizzare i blocchi.

0

Se ho avuto questo problema, probabilmente andare per un approccio come questo:

  • un unico filo che caricherà le immagini, e fa sì che il thread principale per visualizzare i risultati (non un sono grande fan di avere problemi con gli oggetti della GUI)

  • quando viene richiesta una nuova directory ... beh, dipende da come si vogliono gestire le cose. Fondamentalmente, un costrutto di coda standard (variabile di condizione e array) potrebbe essere usato per il thread principale per dire al thread che "questa directory sarà necessaria" passandogli il nome del percorso; il thread controllerà la coda anche quando carica le immagini (come dopo ogni immagine o così) e passa alla nuova directory ogni volta che si visualizza

  • si può creare un oggetto directory-reader che mantenga tutto lo stato, e memorizzare questo indicizzato dal percorso in un dizionario. Quando viene richiesta una nuova directory, controlla prima quel dizionario e crea solo un nuovo oggetto se non ce n'è uno per questa directory. In questo modo, le directory parzialmente caricate resterebbero in attesa fino a quando non saranno nuovamente necessarie e potranno continuare a caricarsi invece di dover ricominciare da capo.

Pseudocodice per il filo:

while (forever) 
    new element = nil 
    if we have an active directory loader 
     tell directory loader to load one image 
     if false then make directory loader inactive 
     lock queue condition 
     if queue has elements 
      new element = retrieve LAST element (we aren't interested in the others) 
      empty queue 
      unlock with status "empty" 
     else 
      unlock queue 
    else 
     lock queue on condition "has elements" 
     new element = retrieve last element 
     empty queue 
     unlock with status "empty" 
    if new element != nil 
     if directory loader for new path does not exist 
      setup new directory loader for new path 
      store in dictionary 
      make it the "active" one 
     else 
      make the current one the "active" 

Per quanto riguarda il caricatore directory, potrebbe sembrare qualcosa di simile:

read one image: 
    if there are still images to read: 
     read, process and store one 
     return true 
    else 
     performSelectorOnMainThread with an "update GUI" method and the image list as parameter 
     return false; 

Questo è solo un rapido schizzo; c'è del codice duplicato nella discussione e il modo in cui l'ho scritto aggiornerà solo la GUI dopo che tutte le immagini sono state lette, invece di farle apparire mentre le leggiamo. Dovrai copiare l'attuale elenco di immagini o aggiungere la sincronizzazione se vuoi farlo.

Problemi correlati