как прервать спящий поток в pthread?

у меня есть нить, которая долго спит, потом просыпается, чтобы что-то сделать, потом снова спит, вот так:

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

как я могу сделать этот поток выход изящно и быстро?

Я пытался использовать pthread_cancel(), но спящие потоки не могут быть отменены. Я также попытался изменить состояние цикла while, но для выхода все равно потребуется много времени. И я не хочу использовать pthread_kill(), так как он может убить поток, когда он работает.

Итак, есть ли хорошие идеи?

3 ответов


в качестве альтернативы sleep, вы могли бы использовать pthread_cond_timedwait с таймаутом 1000 мс. Затем, когда вы хотите выйти, сигнализируйте переменной условия.

это похоже на то, как вы могли бы сделать это в C#/Java, используя wait и notify.


классической переменной условия UNIX является 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);
    }

Да, это много неудобного (и непроверенного) кода. Вероятно, вы должны просто использовать pthread_cond_wait требует pthread_mutex_t и pthread_cond_t но гораздо легче.


вы использовали pthread_cleanup_push и pop? Отмена с помощью pthread_cancel не работает без них. Вы должны использовать их в парах так же, как я сделал в примере ниже. если вы забудете один, он не будет компилироваться (причудливые макросы, один имеет " { " , а другой -"}"). Вы даже можете вложить различные уровни очистки / pops. Во всяком случае, они устанавливают точку прыжка в длину, которая отменяет прыжки, когда происходит отмена (довольно круто). Кроме того, если ваша тестовая программа не ждет запуска или остановки потока, вы может не заметить, что отмена происходит.

пример:

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

программа:

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

обратите внимание, как отмена выходит из ThreadProc в точке отмены sleep () и выполняет только функцию unwind ().