如何实现主线程超时后终止等待阻塞的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
相关产品推荐
相关产品推荐

