You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现主线程超时后终止等待阻塞的pthread线程?

解决线程中超时等待阻塞系统调用的问题

问题描述

我想要编写一个程序,避免阻塞系统调用导致永久挂起(并非要解决停机问题)。程序需要调用阻塞系统调用,若超过指定时间则放弃等待。

我尝试的方案是:主线程创建分离线程执行可能永久阻塞的系统调用,主线程等待指定时间,超时则放弃等待继续处理其他事务。主线程通过pthread_cond_timedwait等待子线程的完成信号,超时时间由程序参数指定。

原始代码

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

运行结果

$ 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

程序虽输出了超时信息,但实际耗时3秒而非指定的1秒,主线程必须等待子线程完成才会结束。


问题根源

pthread_cond_timedwait超时返回时,会重新获取互斥锁才会返回。此时子线程已持有互斥锁(执行sleep前完成了加锁),主线程会被阻塞在重新加锁的步骤,直到子线程释放互斥锁才能继续执行,导致主线程无法在超时后立即处理后续事务。


解决方案

方案1:主线程超时后释放互斥锁,允许子线程自行结束

修改主线程的超时分支,先释放互斥锁再退出或处理其他事务,避免子线程因锁阻塞:

case ETIMEDOUT:
    puts("MAIN: Thread did not finish in time");
    // 释放互斥锁,避免子线程阻塞
    pthread_mutex_unlock(&thread_data.mutex);
    // 此处可添加其他事务处理逻辑
    return EXIT_SUCCESS;

同时在其他分支(成功或失败)也需释放互斥锁,避免资源泄漏。

修改后的完整代码

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

    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");
            pthread_mutex_unlock(&thread_data.mutex);
            return EXIT_SUCCESS;
        case ETIMEDOUT:
            puts("MAIN: Thread did not finish in time");
            // 释放互斥锁,避免子线程阻塞
            pthread_mutex_unlock(&thread_data.mutex);
            // 此处可添加其他事务处理逻辑
            return EXIT_SUCCESS;
        default:
            printf("MAIN: Failed to wait for thread to finish: %s", strerror(ret));
            pthread_mutex_unlock(&thread_data.mutex);
            return EXIT_FAILURE;
    }
}

测试结果

$ 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
MAIN: Thread did not finish in time
./prog 1  0.00s user 0.00s system 0% cpu 1.006 total

主线程在1秒后立即退出,子线程会在后台自行完成执行,不影响主线程的后续处理。


方案2:主线程超时后主动终止子线程

如果希望子线程在主线程超时后立即停止,可使用pthread_cancel:系统调用(如sleep)是默认的取消点,子线程会在阻塞调用处被终止。

修改主线程超时分支:

case ETIMEDOUT:
    puts("MAIN: Thread did not finish in time");
    // 取消子线程
    int cancel_ret = pthread_cancel(thread_data.thread);
    if (cancel_ret != 0) {
        printf("MAIN: Failed to cancel thread: %s\n", strerror(cancel_ret));
    }
    // 释放互斥锁
    pthread_mutex_unlock(&thread_data.mutex);
    return EXIT_SUCCESS;

此方案下子线程会被立即终止,不会继续执行后续逻辑。


内容的提问来源于stack exchange,提问作者larsewi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 22:01:03