2009-07-18 17 views
8

Solía ​​pensar que era una persona razonablemente inteligente.
La "guía de programación de enhebrado" de apple ha hecho añicos mi egoísmo autodestructivo.Objective-C ejecutar lazo para detener y reiniciar el método?

Tengo un método que quiero ejecutar repetidamente en una secuencia secundaria, en el siguiente ejemplo lo he llamado doStuff:
Quiero poder detener e iniciar repetidamente las llamadas repetidas de este método.

el código comienza el hilo.
si el valor stuffToDo booleana es verdadera,
A continuación, llama hacerTarea:
lo contrario
Tiene un poco de descanso.
luego se repite hasta que digo que se detenga

Mi código actual parece un desperdicio, porque sigue comprobando "cosas que hacer" incluso cuando no hay nada que hacer.

Podría deshacerme de stuffToDo y solo generar y cancelar el hilo según sea necesario.
Esto también parece un desperdicio, y significa que tendría que tener cuidado de no generar accidentalmente un nuevo hilo cuando ya tengo uno en ejecución.

Estoy seguro de que la respuesta a la solución de manera eficiente mi situación puede ser encontrado en alguna parte de la sección "Gestión de bucle de ejecución" de "enhebrado guía de programación" de Apple
quizá se trata de fuentes de entrada personalizados

pero estoy realmente encontrar este documento desafiante.
Es como si este documento está generando demasiados hilos en mi cerebro y la computación se detiene.

enum eRenderThreadMode 
{ 
    render_not_started, 
    render_run, 
    render_cancel, 
    render_finished 
}; 


- (IBAction) startThread:(id)sender 
{ 
    self.renderThreadMode = render_run; 
    label.text = @"doing stuff"; 
    [NSThread detachNewThreadSelector:@selector(keepDoingStuff) toTarget:self withObject:nil]; 

} 

- (void)keepDoingStuff 
{ 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    while (renderThreadMode == render_run) 
    { 
     if(stuffToDo) 
     { 
      [self doStuff]; 
     } 
     else 
     { 
      [NSThread sleepForTimeInterval:kLittleRest]; 
     } 
    } 
    self.renderThreadMode = render_finished; 
    [pool release]; 
} 


- (IBAction)stopThread:(id)sender 
{ 
    self.renderThreadMode = render_stop; 

    while (self.renderThreadMode == render_cancel) 
    { 
     [NSThread sleepForTimeInterval:kLittleRest]; 
    } 
} 

Respuesta

7

Puede usar un objeto de sincronización cuya secuencia secundaria duerme. This Apple page indica que hay una instalación llamada Condiciones que podría hacer lo que desee. El uso de una condición u objeto de sincronización similar permitirá que el hilo solo se active cuando haya que trabajar (o cuando sea el momento de que el hilo muera).

+0

De acuerdo. Use un NSConditionLock. Esta es una construcción de enhebrado estándar. –

+0

gracias, parece que va a hacer exactamente lo que quiero –

+0

Utilicé NSCondition, como se sugirió, en lugar de NSConditionLock. ¿Realmente quisiste decir ConditionLock en vez de esa Condición? Si es así, ¿cómo te imaginas implementar esto usando NSConditionLock. No he trabajado con subprocesos objetivos c antes, y estoy interesado en conocer diferentes enfoques. –

7

Sí, tiene razón en que desea utilizar un runloop, lo que se está perdiendo es cómo configurar todo esto. Voy a modificar tu publicación y explicar lo que está sucediendo. No se preocupe si se le intimida, es complicado y hay algunas trampas que sólo aprenden acerca de la experiencia

- (IBAction) startThread:(id)sender 
{ 
    self.renderThreadMode = render_run; 
    label.text = @"doing stuff"; 
    self.backgroundThread = [[NSThread alloc] initWithTarget:self selector:@selector(keepDoingStuff) object:nil]; 
    [self.backgroundThread start];  
} 

//Okay, this is where we start changing stuff 
- (void)keepDoingStuff 
{ 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

     //A runloop with no sources returns immediately from runMode:beforeDate: 
     //That will wake up the loop and chew CPU. Add a dummy source to prevent 
     //it. 

     NSRunLoop *runLopp = [NSRunLoop currentRunLoop]; 

     NSMachPort *dummyPort = [[NSMachPort alloc] init]; 
     [runLoop addPort:dummyPort forMode:NSDefaultRunLoopMode]; 
     [dummyPort release]; 
     [pool release]; 

     while (1) 
     { 
       NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; 
       [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
       [loopPool drain]; 
     } 
} 

bien, así que en este punto se debe buscar en el código anterior y pensando "Bueno, eso puede ser un buen hilo para dormir, pero no hace nada. Y eso es cierto, pero dado que tiene un runloop activo, podemos hacer cualquier cosa que esté basada en runloop, incluir performSelector: onThread: withObject: waitUntilDone:

- (void) doStuffOnBackgroundThread 
{ 
    [self performSelector:@selector(doStff) onThread:self.backgroundThread withObject:nil waitUntilDone:NO]; 
} 

Cuando llama al método anterior en su hilo principal (o en cualquier otro hilo) reparará los diversos argumentos y pondrá en cola el runloop del specif hilo infectado, despertando según sea necesario. En este caso, hará que self.backgroundThread se active desde runMode: beforeDate :, execute-doStuff, luego retrocede el ciclo y vuelve a la espera de espera en runMode: beforeDate :.Si quieres poder derribar el hilo, puedes configurar una variable en el ciclo while como la que tenías en tu código, aunque recuerda que el hilo se iría si está activo a menos que lo despiertes, por lo que probablemente sea mejor encapsularlo eso en un método que establece la variable de control a través de un performSelector: onThread: withObject: waitUntilDone :, como un plus que significará que la variable solo se establecerá desde el hilo de fondo, lo que simplifica los problemas de sincronización.

Bien, entonces creo que eso resuelve tu problema, así que es hora de hacer el enchufe obligatorio: ¿Estás seguro de que quieres hacer esto con los hilos? NSOperation y NSOperationQueue pueden ser una solución mucho más simple que se encarga de todos los problemas de enhebrado si todo lo que necesita hacer es ocasionalmente poner en cola algunos datos que se han procesado. Planificarán el trabajo, administrarán las dependencias y crearán y eliminarán hilos, además de ocuparse de todo el runloop de despertar/dormir.

+0

gracias por la gran respuesta detallada. En se pospuso usar NSOperation, porque tenía la impresión de que NSOperation está diseñado para procesar una cantidad finita de trabajo, mientras que cuando quiero iniciar un hilo y mantenerlo funcionando durante un período indefinido, (mínimo 2 segundos, máximo hasta la batería funciona en plano?) así que sé que no estoy seguro de que necesito usar hilos sobre NSOperation, simplemente se basó en un vago sentido de que sentí que valía más la pena investigar –

+0

Puede hacer cualquiera de los dos. Un NSOperationQueue seguirá ejecutándose hasta que se vacíe, pero puede mantenerlo y lanzar más NSOperations en cualquier punto y hacer que se encargue de eso. –

+0

Gracias Louis, un par de preguntas sobre lo anterior, ¿de dónde viene runLoop? también en mi caso cuando el hilo está funcionando, quiero que siga llamando doStuff: lo más rápido que pueda sin mi intervención, así que podría cambiar la estructura así que llamo algo así como: [self performSelector: @selector (doAsMuchStuffAsYouCanUntilItellYouToStop) onThread: self.backgroundThread withObject: nil waitUntilDone: NO]; –

Cuestiones relacionadas