2011-01-24 27 views
7

tengo hilo que duerme durante mucho tiempo, entonces se despierta para hacer algo, luego dormir de nuevo, como esto:cómo terminar un hilo de dormir en pthread?

while(some_condition) 
{ 
    // do something 
    sleep(1000); 
} 

¿Cómo podría hacer esta salida de rosca con gracia y rápidamente?

Intenté usar pthread_cancel(), pero los hilos que duermen no se pudieron cancelar. También traté de cambiar la condición del ciclo while, pero tomará mucho tiempo para salir. Y no quiero usar pthread_kill(), ya que puede matar el hilo cuando está funcionando.

Entonces, ¿hay alguna buena idea?

+0

bueno, creo que me equivoco, sleep() es un punto de cancelación definido por posix.1 – qiuxiafei

+1

Solo una nota - 'pthread_kill' no mata un hilo. Envía una señal a un hilo. Si la acción de esa señal es terminar el proceso (por ejemplo, la acción predeterminada de 'SIGTERM', o la acción no bloqueable de' SIGKILL') entonces terminará * todo el proceso *, no el hilo de destino. Realmente, el único momento en que 'pthread_kill' es útil es si ha instalado un manejador de señal de interrupción y quiere interrumpir un syscall que está bloqueado en un hilo en particular (que podría ser una posible solución a su pregunta). –

Respuesta

11

Como alternativa a sleep, puede usar pthread_cond_timedwait con un tiempo de espera de 1000 ms. Luego, cuando quieras salir, señala la variable de condición.

Esto es similar a cómo puede hacer esto en C#/Java usando wait and notify.

+1

Correcto. Esta es la manera simple y no-picantes de hacerlo. – caf

+0

Esto es correcto, pero la cancelación es más fácil y tal vez un poco más eficiente. Por supuesto, si llamas a 'pthread_cancel' en el hilo, debes asegurarte de que no pueda ser cancelado en un punto que no debería ser cancelado. Llamar a 'pthread_setcancelstate' para deshabilitar la cancelación cuando se inicia el hilo, y solo habilitarlo justo antes del' sleep', sería un enfoque seguro. –

+0

IMO, 'pthread_cancel()' puede ser preferible ya que 'sleep()' ya es un punto de cancelación.Y asegúrese de llamar a 'pthread_cleanup_push()' y 'pthread_cleanup_pop()' para configurar los manejadores de limpieza si tiene recursos asignados. – zeekvfu

0

La variable de condición clásica de UNIX es self-pipe.

int fds[2]; 
pipe2(fds, O_NONBLOCK); // requires newish kernel and glibc; use pipe + 2*fcntl otherwise 

child: 
    while (some_condition) { 
     // do something 
     struct pollfd pd = { .fd = fds[0], .events = POLLIN }; 
     int rc; 
     char c; 
     while ((rc = poll(&pd, 1, 1000000)) == -1 && errno == EINTR) 
      // not entirely correct; 1000000 should be decreased according to elapsed time when repeating after a signal interruption 
      ; 
     if (rc > 0 && (pd.revents & POLLIN) && read(fds[0], &c, 1) >= 0) 
      break; 
    } 

parent: 
    cancel() { 
     char c = 0; 
     write(fds[1], &c, 1); 
    } 

Sí, es un código bastante complicado (y no probado). Probablemente deberías simplemente usar pthread_cond_wait, requiere un pthread_mutex_t y un pthread_cond_t pero es mucho más fácil.

0

¿Usaste pthread_cleanup_push y pop? Cancelar con pthread_cancel no funciona sin ellos. Debe usarlos en pares como lo hice en el ejemplo a continuación. si olvida uno no compilará (macros de fantasía, uno tiene el '{' y el otro tiene el '}'). Incluso puedes anidar diferentes niveles de limpieza/pops. De todos modos, establecen un punto de salto largo que cancela los saltos cuando ocurre la cancelación (muy bueno). Además, si su programa de prueba no espera a que la secuencia se inicie o se detenga, es posible que no note que se está cancelando.

Ejemplo:

#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 

static void *ThreadProc(void * arg); 
static void unwind(__attribute__ ((unused)) void *arg); 

int _fActive = 0; 

int main(int argc, char** argv) 
{ 
pthread_t Thread; 
int  nRet; 

    nRet = pthread_create(&Thread, NULL, ThreadProc, NULL); 
    printf("MAIN: waiting for thread to startup...\n"); 
    while (_fActive == 0) 
     nanosleep(&(struct timespec){ 0, 0}, NULL); 
    printf("MAIN: sending cancel...\n"); 
    nRet = pthread_cancel(Thread); 

    printf("MAIN: waiting for thread to exit...\n"); 
    while (_fActive) 
     nanosleep(&(struct timespec){ 0, 0}, NULL); 

    printf("MAIN: done\n"); 
    return 0; 
} 

static void unwind(__attribute__ ((unused)) void *arg) 
{ 
    // do some cleanup if u want 
    printf("THREAD: unwind (all threads, canceled or normal exit get here)\n"); 
    _fActive = 0; 
} 

static void *ThreadProc(void * arg) 
{ 
    pthread_cleanup_push(unwind, arg); 
    // optional : pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 
    printf("THREAD: Enter Sleep\n"); 
    _fActive = 1; 
    sleep(1000000); 
    printf("THREAD: Exit Sleep (canceled thread never gets here)\n"); 
    pthread_cleanup_pop(1); 

    printf("THREAD: Exit (canceled thread never gets here)\n"); 
    return NULL; 
} 

La salida del programa:

MAIN: waiting for thread to startup... 
THREAD: Enter Sleep 
MAIN: sending cancel... 
MAIN: waiting for thread to exit... 
THREAD: unwind (all threads, canceled or normal exit get here) 
MAIN: done 

Aviso cómo el cancelar golpes de ThreadProc en el sueño punto de cancelar() y ejecuta sólo la función de desenrollado().

+0

Puede usar 'pthread_cancel' muy bien sin controladores de limpieza, pero si ha asignado recursos que deberían liberarse, hacerlo provocaría una pérdida de recursos. –

+0

Estoy seguro de que tiene razón cuando se trata de este tema. Creía anteriormente que cleanup_push y pop configuraban el salto largo que invocó pthread_cancel. Supongo que estás diciendo que hay una configuración predeterminada por pthread_create. ¿Lo tengo bien? – johnnycrash

+0

Al final de la cadena de controladores de limpieza, se ejecuta el código de salida de subproceso interno (que incluye destructores de datos específicos de subprocesos y posiblemente otros elementos específicos de la implementación). Si no hay controladores de limpieza, la ejecución pasa directamente allí. Tanto la cancelación como una llamada a 'pthread_exit' por el hilo en sí tienen este efecto. –