可能原因分析
- 信号竞争问题:在多线程环境下,信号可能会在任意时刻到达,而不同线程可能同时访问共享资源。如果信号处理函数和其他线程同时操作共享资源,可能会导致数据竞争,使得清理操作执行不正确。
- 信号掩码设置不当:每个线程都有自己的信号掩码。如果主线程在创建子线程后没有正确设置信号掩码,子线程可能会意外地捕获信号,干扰主线程的信号处理逻辑。
- 线程同步问题:清理操作可能依赖于某些子线程的完成状态。如果在清理操作执行时,相关子线程还未完成任务,可能导致清理不完整或错误。
代码解决方案
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
// 全局变量,用于标记是否需要清理
volatile sig_atomic_t need_cleanup = 0;
// 共享资源
int shared_resource = 0;
// 线程同步互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 线程同步条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 子线程任务函数
void* thread_task(void* arg) {
// 子线程屏蔽SIGUSR1信号
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
// 模拟子线程任务
for (int i = 0; i < 1000000; i++) {
pthread_mutex_lock(&mutex);
shared_resource++;
pthread_mutex_unlock(&mutex);
}
// 任务完成,通知主线程
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
// 信号处理函数
void signal_handler(int signum) {
// 设置清理标记
need_cleanup = 1;
}
int main() {
// 设置信号处理函数
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
// 创建子线程
pthread_t threads[5];
for (int i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, thread_task, NULL);
}
// 主线程等待子线程完成
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
// 等待信号触发清理操作
while (!need_cleanup) {
sleep(1);
}
// 执行清理操作
pthread_mutex_lock(&mutex);
printf("Cleaning up shared_resource: %d\n", shared_resource);
shared_resource = 0;
pthread_mutex_unlock(&mutex);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
代码说明
- 信号处理函数:
signal_handler
函数设置了need_cleanup
标记,表明需要执行清理操作。
- 子线程任务:每个子线程屏蔽
SIGUSR1
信号,避免干扰主线程的信号处理。子线程完成任务后,通过条件变量通知主线程。
- 主线程:主线程等待所有子线程完成任务,然后等待
SIGUSR1
信号。信号触发后,主线程执行清理操作,确保共享资源得到正确清理。同时,使用互斥锁保护共享资源的访问,防止数据竞争。最后销毁互斥锁和条件变量。