面试题答案
一键面试可能出现竞争条件的场景举例
假设在一个基于C语言实现Linux异步I/O异步通知机制的程序中,有多个线程会同时处理异步I/O完成后的通知。当I/O操作完成,内核会发送通知信号,多个线程可能同时接收到这个信号并尝试处理。例如,在处理函数中可能有对共享资源(如全局计数器)的操作:
#include <stdio.h>
#include <pthread.h>
// 全局计数器
int counter = 0;
// 模拟I/O完成处理函数
void io_completion_handler() {
counter++;
printf("Counter incremented to %d\n", counter);
}
// 线程函数
void* thread_function(void* arg) {
// 模拟接收到I/O完成通知
io_completion_handler();
return NULL;
}
在这个例子中,如果多个线程同时调用io_completion_handler
,由于对counter
的操作不是原子的,就会出现竞争条件,导致counter
的值可能并不是预期的正确累加结果。
使用互斥锁避免竞争条件
互斥锁(pthread_mutex_t
)可以用来保证同一时间只有一个线程能够访问共享资源。修改上述代码如下:
#include <stdio.h>
#include <pthread.h>
// 全局计数器
int counter = 0;
// 互斥锁
pthread_mutex_t mutex;
// 模拟I/O完成处理函数
void io_completion_handler() {
// 加锁
pthread_mutex_lock(&mutex);
counter++;
printf("Counter incremented to %d\n", counter);
// 解锁
pthread_mutex_unlock(&mutex);
}
// 线程函数
void* thread_function(void* arg) {
// 模拟接收到I/O完成通知
io_completion_handler();
return NULL;
}
int main() {
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
pthread_t threads[10];
for (int i = 0; i < 10; i++) {
pthread_create(&threads[i], NULL, thread_function, NULL);
}
for (int i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
在io_completion_handler
函数中,通过pthread_mutex_lock
和pthread_mutex_unlock
来保护对counter
的操作,这样就避免了竞争条件。
使用信号量避免竞争条件
信号量(sem_t
)可以控制同时访问共享资源的线程数量,也能用来避免竞争条件。以下是使用信号量修改后的代码:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
// 全局计数器
int counter = 0;
// 信号量
sem_t sem;
// 模拟I/O完成处理函数
void io_completion_handler() {
// 等待信号量
sem_wait(&sem);
counter++;
printf("Counter incremented to %d\n", counter);
// 释放信号量
sem_post(&sem);
}
// 线程函数
void* thread_function(void* arg) {
// 模拟接收到I/O完成通知
io_completion_handler();
return NULL;
}
int main() {
// 初始化信号量,初始值设为1
sem_init(&sem, 0, 1);
pthread_t threads[10];
for (int i = 0; i < 10; i++) {
pthread_create(&threads[i], NULL, thread_function, NULL);
}
for (int i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
// 销毁信号量
sem_destroy(&sem);
return 0;
}
在这个例子中,通过sem_wait
和sem_post
函数配合,保证同一时间只有一个线程能进入对共享资源counter
的操作区域,从而避免竞争条件。