可能出现的竞态条件
- 主线程与信号处理函数之间:主线程在读写共享资源时,可能被信号打断,信号处理函数随即访问共享资源,导致数据不一致。例如,主线程正在进行一个多步骤的写操作(如先修改部分数据,再修改另一部分以完成完整更新),信号处理函数此时介入访问共享资源,可能获取到不完整的数据,或者对不完整的数据状态进行进一步修改,破坏数据的完整性。
解决方法
- 使用互斥锁(Mutex)
- 在主线程读写共享资源前,先获取互斥锁,操作完成后释放互斥锁。
- 在信号处理函数访问共享资源前,同样获取互斥锁,操作结束后释放。
- 示例代码(简化示意):
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex;
int shared_resource;
void sig_handler(int signum) {
pthread_mutex_lock(&mutex);
// 信号处理函数中对共享资源的操作
shared_resource++;
pthread_mutex_unlock(&mutex);
}
void* thread_function(void* arg) {
pthread_mutex_lock(&mutex);
// 线程中对共享资源的操作
shared_resource = 10;
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t thread;
pthread_mutex_init(&mutex, NULL);
signal(SIGUSR1, sig_handler);
pthread_create(&thread, NULL, thread_function, NULL);
// 主线程其他操作
pthread_join(thread, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
- 使用读写锁(Read - Write Lock):如果主线程读操作较多,写操作较少,可以使用读写锁。主线程读共享资源时获取读锁,写时获取写锁;信号处理函数如果只写,获取写锁。这样可以允许多个读操作同时进行,但写操作时会独占共享资源,避免读写和写写冲突。
- 使用原子操作:对于简单的共享资源类型(如整数),可以使用原子操作函数(如
__sync_fetch_and_add
等,具体取决于编译器和平台)。原子操作保证操作的原子性,即不会被中断,避免竞态条件。例如在信号处理函数和主线程中对shared_resource
进行自增操作时可以使用原子操作函数。但原子操作适用场景相对有限,对于复杂数据结构可能无法直接使用。