2011-01-24 16 views
7

Mam wątku, który śpi na długi czas, a potem budzi się coś zrobić, a potem znowu spać, tak:jak zakończyć wątek spania w pthread?

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

Jak mogłem zrobić to wyjście gwintu wdzięcznie i szybko?

Próbowałem użyć pthread_cancel(), ale wątki wątkowe nie mogły zostać anulowane. Próbowałem także zmienić stan pętli while, ale jego zamknięcie zajmie dużo czasu. I nie chcę używać pthread_kill(), ponieważ może zabić wątek, gdy działa.

Czy są jakieś dobre pomysły?

+0

Cóż, myślę, że popełniłem błąd, sleep() to punkt anulowania ograniczony przez posix.1 – qiuxiafei

+1

Po prostu notatka - 'pthread_kill' nie zabija wątku. Wysyła sygnał do wątku. Jeśli działanie tego sygnału ma na celu zakończenie procesu (np. Domyślna akcja 'SIGTERM' lub działanie odblokowalne' SIGKILL'), to zakończy * cały proces *, a nie docelowy wątek. Naprawdę jedyny czas, gdy 'pthread_kill' jest użyteczny, to jeśli zainstalowałeś przerywnik sygnału i chcesz przerwać blokadę systemu, która jest zablokowana w konkretnym wątku (który byłby potencjalnym rozwiązaniem twojego pytania). –

Odpowiedz

11

Jako alternatywę dla sleep, można użyć pthread_cond_timedwait z limitem 1000 ms. Następnie, kiedy chcesz wyjść, zasygnalizuj zmienną warunku.

Jest to podobne do tego, jak można to zrobić w C#/Java za pomocą wait i notify.

+1

Prawidłowo. Jest to prosty i niewymyślny sposób na zrobienie tego. – caf

+0

Jest to poprawne, ale anulowanie jest łatwiejsze i być może nieco bardziej wydajne. Oczywiście jeśli nazwiesz 'pthread_cancel' w wątku, musisz upewnić się, że nie można go anulować w punkcie, w którym nie powinien. Wywołanie 'pthread_setcancelstate' w celu wyłączenia anulowania po uruchomieniu wątku i włączeniu go tylko przed" snem "byłoby bezpiecznym podejściem. –

+0

IMO, 'pthread_cancel()' może być preferowane, ponieważ 'sleep()' jest już punktem anulowania.I pamiętaj, aby wywołać 'pthread_cleanup_push()' i 'pthread_cleanup_pop()', aby skonfigurować procedury czyszczenia, jeśli masz przydzielone zasoby. – zeekvfu

0

Klasyczną zmienną warunku UNIX jest 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); 
    } 

Tak, jest dużo skrzypiec (i nietestowany) kod. Powinieneś po prostu użyć pthread_cond_wait, wymaga to pthread_mutex_t i pthread_cond_t, ale jest znacznie łatwiejsze.

0

Czy używałeś pthread_cleanup_push i pop? Anulowanie za pomocą pthread_cancel nie działa bez nich. Musisz używać ich parami, tak jak to zrobiłem w poniższym przykładzie. jeśli zapomnisz jednego, to nie skompilujesz (fantazyjne makra, jeden ma "{", a drugi ma "}"). Możesz nawet zagnieździć różne poziomy porządków/popów. W każdym razie, ustawiają one punkt skoku długiego, który anuluje skoki, kiedy nastąpi anulowanie (całkiem fajnie). Ponadto, jeśli twój program testowy nie czeka na rozpoczęcie lub zakończenie wątku, możesz nie zauważyć anulowania.

Przykład:

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

wyjście Program:

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 

Wskazówki, jak anulować ciosy z ThreadProc przy unieważnieniu punktu snu() i wykonuje tylko funkcję odwijania().

+0

Możesz użyć 'pthread_cancel' po prostu dobrze, bez procedur czyszczenia, ale jeśli masz przydzielone zasoby, które powinny zostać zwolnione, spowoduje to wyciek zasobów. –

+0

Jestem pewien, że masz rację, jeśli chodzi o ten temat. Wcześniej sądziłem, że cleanup_push i pop ustawiają longjump, który wywołał pthread_cancel. Domyślam się, że mówisz, że istnieje domyślna konfiguracja pthread_create. Czy mam to prawo? – johnnycrash

+0

Na końcu łańcucha procedur obsługi czyszczenia uruchamiany jest kod zakończenia wewnętrznego implementacji (który zawiera destruktory danych specyficzne dla wątków i prawdopodobnie inne elementy zależne od implementacji). Jeśli nie ma procedur obsługi czyszczenia, wykonanie przechodzi bezpośrednio tam. Zarówno anulowanie, jak i wywołanie 'pthread_exit' przez sam wątek mają ten efekt. –