2011-01-24 16 views
7

Ho filo che può ospitare per lungo tempo, poi si sveglia per fare qualcosa, poi dormire di nuovo, in questo modo:come terminare un thread dormiente in pthread?

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

Come potrei fare questa uscita discussione con grazia e rapidamente?

Ho provato a utilizzare pthread_cancel(), ma non è stato possibile annullare i thread in sospensione. Ho anche provato a cambiare la condizione del ciclo while, ma ci vorrà ancora molto tempo per uscire. E non voglio usare pthread_kill(), poiché potrebbe interrompere il thread quando funziona.

Quindi, ci sono buone idee?

+0

beh, penso di fare un errore, sleep() è un punto di annullamento limitato da posix.1 – qiuxiafei

+1

Solo una nota - 'pthread_kill' non uccide un thread. Invia un segnale a un thread. Se l'azione di quel segnale è di terminare il processo (ad esempio, l'azione predefinita di 'SIGTERM', o l'azione sbloccabile di' SIGKILL'), terminerà * l'intero processo *, non il thread di destinazione. In realtà l'unica volta che 'pthread_kill' è utile è se hai installato un gestore di segnale di interruzione e vuoi interrompere un syscall che è bloccato in un particolare thread (che sarebbe una potenziale soluzione alla tua domanda). –

risposta

11

In alternativa a sleep, è possibile utilizzare pthread_cond_timedwait con un timeout di 1000 ms. Quindi quando vuoi uscire, segnala la variabile di condizione.

Questo è simile a come si potrebbe fare ciò in C#/Java usando wait e notify.

+1

corretto. Questo è il modo semplice e non aggressivo per farlo. – caf

+0

Questo è corretto, ma la cancellazione è più semplice e forse leggermente più efficiente. Naturalmente se chiami 'pthread_cancel' sul thread, devi assicurarti che non possa essere cancellato in un punto che non dovrebbe. Chiamando 'pthread_setcancelstate' per disabilitare la cancellazione all'avvio del thread, e abilitandolo appena prima del' sleep', sarebbe un approccio sicuro. –

+0

IMO, 'pthread_cancel()' può essere preferibile poiché 'sleep()' è già un punto di cancellazione.E assicurati di chiamare 'pthread_cleanup_push()' e 'pthread_cleanup_pop()' per impostare i gestori di cleanup se hai assegnato risorse. – zeekvfu

0

La variabile di condizione UNIX classica è 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ì, è un codice molto complesso (e non testato). Probabilmente dovresti semplicemente usare pthread_cond_wait, richiede un pthread_mutex_t e un pthread_cond_t ma è molto più facile.

0

Hai usato pthread_cleanup_push e pop? Annullare con pthread_cancel non funziona senza di loro. Devi usarli a coppie come ho fatto nell'esempio qui sotto. se ne dimentichi uno non verrà compilato (macro fantasiose, uno ha il '{' e l'altro ha il '}'). Puoi persino annidare diversi livelli di pulizia/pop. Ad ogni modo, impostano un punto di salto in lungo che annulla i salti a quando si verifica l'annullamento (piuttosto freddo). Inoltre, se il programma di test non attende l'avvio o l'interruzione del thread, è possibile che l'annullamento non si verifichi.

Esempio:

#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; 
} 

output del programma:

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 

Notate come il annullare soffia fuori ThreadProc al punto di sonno cancel() ed esegue solo la funzione di svolgimento().

+0

È possibile utilizzare 'pthread_cancel' senza problemi con i gestori di cleanup, ma se si sono allocate risorse da liberare, ciò causerebbe una perdita di risorse. –

+0

Sono sicuro che hai ragione quando si tratta di questo argomento. In precedenza avevo creduto che cleanup_push e pop impostassero il longjump che invocava pthread_cancel. Immagino tu stia dicendo che c'è un setup predefinito di pthread_create. Ho ragione? – johnnycrash

+0

Alla fine della catena di gestori cleanup, viene eseguito il codice di uscita del thread interno all'implementazione (che include i distruttori del thread specifico-dati e probabilmente altre risorse specifiche dell'implementazione). Se non ci sono gestori di cleanup, l'esecuzione passa direttamente lì. Sia l'annullamento che una chiamata a 'pthread_exit' dal thread stesso hanno questo effetto. –

Problemi correlati