2012-06-08 25 views
14

È possibile utilizzare un oggetto NSoperationQueue come coda FIFO seriale impostando il valore maxConcurrentOperationCount su 1?Coda FIFO seriale NSOperationQueue

rilevo che il docs stato ...

Per una coda il cui massimo numero di operazioni simultanee è impostato su 1, questo equivale a una coda seriale. Tuttavia, non si dovrebbe mai fare affidamento sull'esecuzione seriale di oggetti operativi.

Ciò significa che l'esecuzione FIFO non è garantita?

risposta

23

Nella maggior parte dei casi, sarà FIFO. Tuttavia, è possibile impostare dipendenze tra NSOperations in modo tale che un'operazione inviata in anticipo consenta ad altre operazioni di passarla in coda fino a quando le sue dipendenze non sono soddisfatte.

Questa gestione delle dipendenze è il motivo per cui i documenti indicano che la FIFO-ness non può essere garantita. Se non stai usando le dipendenze, però, dovresti fare affidamento su di esso.

Aggiornamento: NSOperation ha anche una proprietà queuePriority, che può anche causare operazioni da eseguire in non-FIFO ordine. L'operazione con priorità più alta senza dipendenze in sospeso verrà sempre eseguita per prima.

Una sottoclasse NSOperation potrebbe anche ignorare -isReady, che potrebbe causare il ritorno in coda.

Quindi l'esecuzione sulla coda è garantita per seriale, in quanto non verrà eseguita più di una operazione alla volta in questa coda. Ma Apple non può garantire FIFO; che dipende da quello che stai facendo con le operazioni che hai messo in

+0

ordine di esecuzione dipende anche la priorità della coda dell'operazione. – JeremyP

+0

Molto vero. Aggiornato. –

+0

Quindi, se le mie operazioni hanno tutte la stessa priorità, non hanno dipendenze e si basano solo sull'implementazione della superclasse di 'isReady' (non sovrascrivo) dovrebbe risultare in una coda FIFO? – Barjavel

-2

per fare un semplice FIFO utilizzando nsInvocationopration Sarà necessario impostare una sola operazione per essere dipendeva dall'altra Utilizzando addDependency:. Metodo

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
NSInvocationOperation *oper1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"1"]; 

NSInvocationOperation *oper2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"2"]; 
NSInvocationOperation *oper3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"3"]; 

[oper2 addDependency:oper1]; 
[oper3 addDependency:oper2]; 
//oper3 depends on oper2 wich depends on oper1 
//order of execution will ber oper1->oper2->oper3 

//Changing the oreder will not change the result 
[queue addOperation:oper2]; 
[queue addOperation:oper3]; 
[queue addOperation:oper1]; 


- (void) doSth:(NSString*)str 
{ 
    NSLog(str); //log will be 1 2 3 
    //When you remove the addDependency calls, the logging result that i got where 
    //different between consecutive runs i got the following 
    //NSLog(str); //log will be 2 1 3 
    //NSLog(str); //log will be 3 1 2 
} 

Nota: se si utilizza NSInvocationOperation quindi impostando il maxConcurrentOperationCount-1 molto probabilmente fare il trucco per voi, dal momento che isReady non sarà modificabile da voi

Ma maxConcurrentOperationCount = 1 non sarebbe una buona soluzione se state programmando di creare i propri sottoclassi di NSOperation

Dal momento in derivati ​​NSOperation si poteva ignorare la funzione isReady e tornare non, (immaginate qualche operazione che avrebbe bisogno di attendere alcuni dati da un server per poter funzionare correttamente) in questi casi che ci si ritorni isReady no fino a quando si è veramente pronti in questi casi sarà necessario aggiungere dependencies tra operations all'interno della coda di

da docs apple , equivale a una coda di serie . Tuttavia, non si dovrebbe mai fare affidamento sull'esecuzione seriale di oggetti operativi. Le modifiche alla disponibilità di un'operazione possono modificare l'ordine di esecuzione risultante

+0

Puoi spiegare perché le dipendenze sono necessarie? –

+0

@BJHomer ho aggiornato la mia risposta per includere alcuni logging –

+0

La coda nell'esempio non ha 'maxConcurrentOperationCount = 1' come l'OP specificato. Questo succede ancora con quel set? –

12

La coda non è FIFO come indicato dalla documentazione.Puoi renderlo rigorosamente FIFO se assicuri che ogni nuova operazione dipenda dall'ultima operazione aggiunta in coda e che possa eseguire solo una singola operazione alla volta. soluzione Omar è corretto, ma più in generale, è possibile effettuare le seguenti operazioni:

NSOperationQueue* queue = [[ NSOperationQueue alloc ] init]; 
queue.maxConcurrentOperationCount = 1; 

NSOperation* someOperation = [ NSBlockOperation blockOperationWithBlock:^(void) { NSLog(@"Done.");} ]; 

if (queue.operations.count != 0) 
    [ someOperation addDependency: queue.operations.lastObject ]; 

Questo funziona perché queue.operations è un array: tutto ciò si aggiunge non è riordinato (non è un NSSet per esempio). Si può anche semplicemente aggiungere una categoria al tuo NSOperationQueue:

@interface NSOperationQueue (FIFOQueue) 
- (void) addOperationAfterLast:(NSOperation *)op; 
@end 

@implementation NSOperationQueue (FIFOQueue) 

- (void) addOperationAfterLast:(NSOperation *)op 
{ 
    if (self.maxConcurrentOperationCount != 1) 
     self.maxConcurrentOperationCount = 1; 

    NSOperation* lastOp = self.operations.lastObject; 
    if (lastOp != nil) 
     [ op addDependency: lastOp ]; 

    [ self addOperation:op]; 
} 

@end 

e utilizzare [coda addOperationAfterLast: myOperation]. queuePriority non ha nulla a che fare con FIFO, è legato alla pianificazione del lavoro.

Modifica: seguendo un commento di seguito, sospendere la coda se anche il controllo del conteggio non è sufficiente. Credo che questo modulo vada bene (dopo aver provato, questo non crea una condizione di competizione e non si blocca).

Alcune informazioni: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/#//apple_ref/occ/instp/NSOperationQueue/suspended

+0

C'è un problema con questo tipo di soluzione. L'ho usato. Occasionalmente, lastObject della coda scompare dopo il controllo su queue.operations.count. È raro, ma succede. E non sono sicuro di come risolverlo. –

+0

Buon punto non l'ho visto, ma sì: se il contatore delle operazioni viene controllato mentre la coda è in esecuzione, potrebbe finire mentre stiamo inserendo la condizione if. Credo che la forma modificata sia migliore e più sicura: (i test qui non si bloccano, mentre un tentativo di sospendere la coda non funziona (non mostrato)). – Daniel

+0

Questa è la soluzione a cui sono arrivato anch'io; crea una variabile locale, impedendo il rilascio di NSOperation. –