C语言 如何等待阻塞线程直到达到超时间隔

bnl4lu3b  于 2023-02-18  发布在  其他
关注(0)|答案(1)|浏览(252)

我正在尝试编写一个程序,可以防止在阻塞系统调用时永远停止。请注意,我不是试图解决停止问题。程序应该尝试调用阻塞系统调用,然后在超过给定时间间隔后给予。
我试过写一个程序,在这个程序中我产生一个线程,这个线程试图执行可能永远阻塞的系统调用。MAIN线程等待一个指定的时间间隔,等待所产生的线程完成。如果超过了这个时间间隔,MAIN线程应该给予并继续处理其他紧迫的事情。
在下面的代码中,MAIN线程派生了一个分离的线程来完成这项工作。这是为了避免不得不加入一个可能永远停止的线程。
接下来,MAIN线程等待派生线程使用pthread_cond_timedwait发出的条件(“I 'm finished!”),以便在超过指定的时间间隔(作为程序参数给出)后超时。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

struct ThreadData {
    pthread_attr_t attr;
    pthread_t thread;
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    bool result;
};

static void *ThreadRoutine(void *data) {
    struct ThreadData *thread_data = data;

    puts("THREAD: Locking mutex");
    int ret = pthread_mutex_lock(&thread_data->mutex);
    if (ret != 0) {
        printf("THREAD: Failed to lock mutex: %s\n", strerror(ret));
        return NULL;
    }

    /* Also tried setting cancel state to async, but it did not help */
    // puts("THREAD: Setting cancel state enable");
    // ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    // if (ret != 0) {
    //     printf("Failed to set cancel state enable: %s\n", strerror(ret));
    //     return NULL;
    // }

    // puts("THREAD: Setting cancel type async");
    // ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    // if (ret != 0) {
    //     printf("THREAD: Failed to set cancel type async: %s\n", strerror(ret));
    //     return NULL;
    // }

    puts("THREAD: Blocking for 3 sec");
    sleep(3); /* example blocking syscall */
    thread_data->result = true;

    puts("THREAD: Signalling main thread");
    ret = pthread_cond_signal(&thread_data->cond);
    if (ret != 0) {
        printf("THREAD: Failed to signal main thread: %s\n", strerror(ret));
        return NULL;
    }

    puts("THREAD: Unlocking mutex");
    ret = pthread_mutex_unlock(&thread_data->mutex);
    if (ret != 0) {
        printf("THREAD: Failed to unlock mutex: %s\n", strerror(ret));
        return NULL;
    }

    puts("THREAD: Returning from thread routine");
    return NULL;
}

int main(int argc, char **argv) {
    if (argc <= 1) {
        puts("Missing argument");
        return EXIT_FAILURE;
    }
    const int timeout = atoi(argv[1]);

    struct ThreadData thread_data;
    thread_data.result = false;

    puts("MAIN: Initializing mutex");
    int ret = pthread_mutex_init(&thread_data.mutex, NULL);
    if (ret != 0) {
        printf("MAIN: Failed to initialize mutex: %s\n", strerror(ret));
        return EXIT_FAILURE;
    }

    puts("MAIN: Initializing condition");
    ret = pthread_cond_init(&thread_data.cond, NULL);
    if (ret != 0) {
        printf("MAIN: Failed to initialize condition: %s\n", strerror(ret));
        return EXIT_FAILURE;
    }

    puts("MAIN: Locking mutex");
    ret = pthread_mutex_lock(&thread_data.mutex);
    if (ret != 0) {
        printf("MAIN: Failed to lock mutex: %s\n", strerror(ret));
        return EXIT_FAILURE;
    }

    puts("MAIN: Initializing thread attributes");
    ret = pthread_attr_init(&thread_data.attr);
    if (ret != 0) {
        printf("Failed to initialize thread attributes\n");
        return EXIT_FAILURE;
    }

    puts("MAIN: Setting thread detach state");
    ret = pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED);
    if (ret != 0) {
        printf("MAIN: Failed to set thread detach state: %s\n", strerror(ret));
        return EXIT_FAILURE;
    }

    puts("MAIN: Creating thread");
    ret = pthread_create(&thread_data.thread, &thread_data.attr, &ThreadRoutine, &thread_data);
    if (ret != 0) {
        printf("MAIN: Failed to create thread: %s\n", strerror(ret));
        return EXIT_FAILURE;
    }

    puts("MAIN: Destroying thread attributes");
    ret = pthread_attr_destroy(&thread_data.attr);
    if (ret != 0) {
        printf("MAIN: Failed to destroy thread attributes: %s\n", strerror(ret));
        return EXIT_FAILURE;
    }

    puts("MAIN: Calculating timeout interval");
    struct timespec ts = {0};
    if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
        printf("MAIN: Failed to get current time: %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    ts.tv_sec += timeout;

    printf("MAIN: Waiting for thread to finish (timeout: %d sec)\n", timeout);
    ret = pthread_cond_timedwait(&thread_data.cond, &thread_data.mutex, &ts);
    switch (ret) {
        case 0:
            printf("MAIN: Thread finished in time; computation evaluated to %s\n", (thread_data.result) ? "true" : "false");
            return EXIT_SUCCESS;
        case ETIMEDOUT:
            puts("MAIN: Thread did not finish in time");
            return EXIT_SUCCESS;
        default:
            printf("MAIN: Failed to wait for thread to finish: %s", strerror(ret));
            return EXIT_FAILURE;
    }
}

运行此程序时,看起来它在pthread_cond_timedwait上被阻塞的时间超过了指定的超时间隔。

$ gcc -g -Wall -Wextra -c main.c -o main.o
$ gcc main.o -o prog -pthread
$ time ./prog 1
MAIN: Initializing mutex
MAIN: Initializing condition
MAIN: Locking mutex
MAIN: Initializing thread attributes
MAIN: Setting thread detach state
MAIN: Creating thread
MAIN: Destroying thread attributes
MAIN: Calculating timeout interval
MAIN: Waiting for thread to finish (timeout: 1 sec)
THREAD: Locking mutex
THREAD: Blocking for 3 sec
THREAD: Signalling main thread
THREAD: Unlocking mutex
THREAD: Returning from thread routine
MAIN: Thread did not finish in time
./prog 1  0.00s user 0.00s system 0% cpu 3.007 total

程序正确地输出线程没有及时完成。但是根据time命令,主线程似乎仍然被阻塞,直到派生线程完成。虽然我希望man线程在超过超时间隔后继续。
我不知道如何使用线程来实现它。另一个选择是尝试使用fork来实现它。但是如果可能的话,我宁愿使用线程。
谢谢:)

yws3nbqq

yws3nbqq1#

正如@Shawn pthread_cond_timedwait提到的,必须重新获取互斥锁。通过在调用“可能永远”阻塞系统调用之前释放派生线程中的互斥锁,代码可以按预期工作。谢谢:)

$ time ./prog 1
MAIN: Initializing mutex
MAIN: Initializing condition
MAIN: Locking mutex
MAIN: Initializing thread attributes
MAIN: Setting thread detach state
MAIN: Creating thread
MAIN: Destroying thread attributes
MAIN: Calculating timeout interval
MAIN: Waiting for thread to finish (timeout: 1 sec)
THREAD: Locking mutex
THREAD: Unlocking mutex
THREAD: Blocking for 3 sec
MAIN: Thread did not finish in time
./prog 1  0.00s user 0.00s system 0% cpu 1.008 total

执行时间现在大约是1秒,正如我所预期的。

相关问题