面试题答案
一键面试可能出现的线程同步问题
- 数据竞争:多个线程同时访问和修改定时器相关数据,可能导致数据不一致。例如,一个线程在更新定时器的时间,另一个线程同时读取该定时器时间,可能读到未完全更新的数据。
- 竞态条件:由于线程执行顺序的不确定性,可能会导致程序出现非预期的行为。比如,一个线程检查定时器是否到期,然后准备执行相关任务,但在检查和执行任务之间,另一个线程修改了定时器状态,导致错误执行。
使用互斥锁和条件变量解决问题的思路
- 互斥锁(
pthread_mutex_t
):用于保护共享数据,确保同一时间只有一个线程可以访问和修改定时器数据。在访问和修改定时器数据前,线程必须先获取互斥锁,操作完成后释放互斥锁。 - 条件变量(
pthread_cond_t
):用于线程间的同步,当定时器满足某个条件(如到期)时,通知等待在条件变量上的线程。一个线程在等待定时器满足条件时,会释放互斥锁并阻塞在条件变量上,当条件满足时,由其他线程唤醒该线程,该线程重新获取互斥锁后继续执行。
关键代码片段
#include <pthread.h>
#include <stdio.h>
// 定义定时器数据结构
typedef struct {
int time;
int is_expired;
} Timer;
Timer timer;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 模拟定时器更新线程
void* update_timer(void* arg) {
while (1) {
pthread_mutex_lock(&mutex);
// 更新定时器数据
timer.time--;
if (timer.time <= 0) {
timer.is_expired = 1;
// 定时器到期,通知等待的线程
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
// 模拟其他操作
sleep(1);
}
return NULL;
}
// 模拟定时器检查线程
void* check_timer(void* arg) {
while (1) {
pthread_mutex_lock(&mutex);
// 等待定时器到期
while (!timer.is_expired) {
pthread_cond_wait(&cond, &mutex);
}
// 定时器到期,执行相关任务
printf("Timer expired, do something.\n");
timer.is_expired = 0;
pthread_mutex_unlock(&mutex);
// 模拟其他操作
sleep(1);
}
return NULL;
}
int main() {
timer.time = 5;
timer.is_expired = 0;
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, update_timer, NULL);
pthread_create(&tid2, NULL, check_timer, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
在上述代码中:
update_timer
函数模拟定时器更新线程,每次更新定时器数据时先获取互斥锁,更新完成后释放互斥锁。当定时器到期时,发送条件变量信号。check_timer
函数模拟定时器检查线程,在等待定时器到期时,先获取互斥锁,然后在条件变量上等待,被唤醒后执行相关任务,最后释放互斥锁。main
函数初始化定时器数据、互斥锁和条件变量,创建并启动两个线程,最后等待线程结束并销毁互斥锁和条件变量。