2010-10-07 3 views
12

Sto provando a utilizzare un metodo da una classe che ho scaricato da qualche parte. Il metodo viene eseguito in background mentre l'esecuzione del programma continua. Non voglio permettere che l'esecuzione del programma continui fino al termine di questo metodo. Come lo faccio?Come attendere il completamento di un thread in Objective-C

risposta

0

Suggerisco di completare la chiamata al metodo di classe nel proprio metodo e di impostare un valore booleano una volta completato. Per esempio:

BOOL isThreadRunning = NO; 
- (void)beginThread { 
    isThreadRunning = YES; 

    [self performSelectorInBackground:@selector(backgroundThread) withObject:nil]; 
} 
- (void)backgroundThread { 
    [myClass doLongTask]; 

    // Done! 
    isThreadRunning = NO; 
} 
- (void)waitForThread { 
    if (! isThreadRunning) { 
     // Thread completed 
     [self doSomething]; 
    } 
} 

Come si desidera gestire attesa è fino a voi: Forse polling con [NSThread sleepForTimeInterval: 1] o simili, o l'invio di un messaggio di sé ogni ciclo di esecuzione.

+0

Ho fatto questo, e funziona, ma impedisce anche che l'HUD venga visualizzato. –

+0

È possibile utilizzare NSTimer per eseguire il polling e mantenere gli aggiornamenti dell'interfaccia utente funzionanti correttamente. Tutto dipende da ciò che ti serve. Per un compito semplice e quindi aggiornare la risposta di Derek sarebbe più ordinata e meno problematica. – v01d

+0

Per un motore di gioco o qualcosa di più in tempo reale, un timer potrebbe essere già presente. Quindi inviare un thread e dimenticarlo potrebbe essere più quello di cui hai bisogno. – v01d

6

La mia prima inclinazione è non fare quello che stai suggerendo. La tecnica che ho usato prima è quella di dare al thread un selettore su un metodo nell'oggetto di origine (che si trova sul thread principale). Quando viene avviato il secondo thread, il thread principale continua a essere eseguito, ma visualizza un indicatore occupato di qualche tipo sul display. Ciò consente all'interazione dell'utente di continuare se necessario.

Quando il secondo thread termina, appena prima che si chiuda, chiama il selettore sul thread principale. Il metodo a cui fa riferimento il selettore rimuove quindi l'indicatore di occupato dal display e dice al thread principale di aggiornarsi, raccogliendo i dati generati dal secondo thread.

L'ho usato con successo per un'app che accede a un servizio Web (sul secondo thread) e quindi aggiorna il display una volta che i dati vengono restituiti senza bloccarlo. Ciò rende l'esperienza dell'utente molto più bella.

+1

Questo è un buon sistema per gli aggiornamenti dell'interfaccia utente; possono essere un dolore. – v01d

8

Utilizzare un NSOperationQueue, come questo
(dalla memoria così perdonare eventuali errori minori - si otterrà l'idea di base):


// ivars 
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init]; 
// count can be anything you like 
[opQueue setMaxConcurrentOperationCount:5]; 

- (void)main 
{ 
    [self doStuffInOperations]; 
} 

// method 
- (void)doStuffInOperations 
{ 
    // do parallel task A 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"a"] autorelease]]; 

    // do parallel task B 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"b"] autorelease]]; 

    // do parallel task C 
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"c"] autorelease]]; 


    [opQueue waitUntilAllOperationsHaveFinished]; 

    // now, do stuff that requires A, B, and C to be finished, and they should be finished much faster because they are in parallel. 
} 

- (void)doSomething:(id)arg 
{ 
    // do whatever you want with the arg here 
    // (which is in the background, 
    // because all NSOperations added to NSOperationQueues are.) 
} 


+1

So che non ho fatto questa domanda, ma questo era quello che stavo cercando con la mia app perché avevo delle condizioni di gara e sapevo che lo erano perché potevo fare clic più velocemente, quindi il codice poteva finire in condizioni di carico parziale sulle viste . Questo ha risolto il mio problema alla grande. Quindi, grazie signore, sento che questo dovrebbe essere contrassegnato come la vera risposta perché non sta usando un lavoro in giro e sta incorporando la codifica thread-safe con la libreria ios. – Rob

+0

Secondo quello che ha detto Rob, questo è esattamente quello che stavo cercando anche io! – AdamM

+1

@AdamM - controlla l'altra risposta che ho postato oggi. È un modo molto più semplice per farlo e GCD è supportato per tutti gli iOS 4 e successivi. Un vantaggio notevole in questo modo è che non si verificheranno arresti anomali del sistema quando gli oggetti vengono deallocati da sotto di te poiché GCD conserva e rilascia automaticamente tutti gli oggetti a cui fai riferimento. – jpswain

35

Ecco un altro modo per farlo utilizzando GCD:



- (void)main 
{ 
    [self doStuffInOperations]; 
} 

- (void)doStuffInGCD 
{ 
    dispatch_group_t d_group = dispatch_group_create(); 
    dispatch_queue_t bg_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"a"]; 
    }); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"b"]; 
    }); 

    dispatch_group_async(d_group, bg_queue, ^{ 
     [self doSomething:@"c"]; 
    }); 


    // you can do this to synchronously wait on the current thread: 
    dispatch_group_wait(d_group, DISPATCH_TIME_FOREVER); 
    dispatch_release(d_group); 
    NSLog(@"All background tasks are done!!"); 


    // **** OR **** 

    // this if you just want something to happen after those are all done: 
    dispatch_group_notify(d_group, dispatch_get_main_queue(), ^{ 
     dispatch_release(d_group); 
     NSLog(@"All background tasks are done!!");   
    }); 
} 

- (void)doSomething:(id)arg 
{ 
    // do whatever you want with the arg here 
} 
+4

Cheers! Questo metodo è ancora meglio – AdamM

+0

+1 per la risposta corretta. – Abizern

+0

@node ninja Ti dispiacerebbe selezionare una risposta per la tua domanda? – jpswain

0

la mia tecnica è quello di verificare se il compito può essere eseguito (se il thread in background è finito), e se non può funzionare, allora io uso GCD di riprovare dopo un ritardo:

- (void)doSomething 
{ 
    if (/* is the other thread done yet? */) { 
    double delayInSeconds = 2.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
     [self doSomething]; 
    }); 
    return; 
    } 

    // do something 
} 
1

Diversi modi tecnici per sincronizzare multi-thread, come ad esempio (mutex-lock), NSCondition (semaforo). Ma sono conoscenze di programmazione comuni per altre lingue (java ...) oltre all'obiettivo-c. Io preferisco di introdurre run loop (speciale in Cocoa) per implementare discussione registrazione:

NSThread *A; //global 
A = [[NSThread alloc] initWithTarget:self selector:@selector(runA) object:nil]; //create thread A 
[A start]; 

- (void)runA  
{ 
  [NSThread detachNewThreadSelector:@selector(runB) toTarget:self withObject:nil]; //create thread B  
  while (1)  
  {  
    if ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) //join here, waiting for thread B  
    {  
      NSLog(@"thread B quit...");  
      break;  
    }  
  }  
} 

- (void)runB  
{  
  sleep(1);  
  [self performSelector:@selector(setData) onThread:A withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];  
} 
3

In questi casi di solito utilizzando la classe NSCondition.

//this method executes in main thread/queue 
- (void)waitForJob 
{ 
    id __weak selfWeak = self; 
    NSCondition *waitHandle = [NSCondition new]; 
    [waitHandle lock]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     [selfWeak doSomethingLongtime]; 
     [waitHandle signal]; 
    }); 
    //waiting for background thread finished 
    [waitHandle waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]]; 
}