面试题答案
一键面试挑战及原因分析
- 竞态条件
- 原因:多个线程同时访问和修改共享资源(如事件队列)时,如果没有适当的同步机制,就会出现竞态条件。例如,一个线程在读取事件队列准备处理事件时,另一个线程可能同时在向队列中添加新事件,导致数据不一致。
- 解决方案:使用互斥锁(
std::mutex
)来保护共享资源。在访问共享资源(如事件队列)前锁定互斥锁,访问结束后解锁。
- 死锁
- 原因:死锁通常发生在多个线程相互等待对方释放资源的情况下。例如,线程A持有锁1并等待锁2,而线程B持有锁2并等待锁1,这样两个线程都无法继续执行。
- 解决方案:采用资源分配图算法(如银行家算法)来检测和避免死锁。在实际编程中,按顺序获取锁是一种简单有效的方法,即所有线程都以相同顺序获取所需的锁。
- 数据一致性
- 原因:当多个线程对共享数据进行读写操作时,由于线程执行的不确定性,可能导致数据处于不一致状态。例如,一个线程正在更新一个复杂数据结构的部分成员,此时另一个线程读取该数据结构,可能得到不完整或错误的数据。
- 解决方案:除了使用互斥锁外,还可以使用读写锁(
std::shared_mutex
)。对于读操作,可以允许多个线程同时进行(共享访问);对于写操作,需要独占访问,以确保数据一致性。
代码示例
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <functional>
// 事件类型
using Event = std::function<void()>;
// 事件队列
std::queue<Event> eventQueue;
// 互斥锁保护事件队列
std::mutex queueMutex;
// 模拟事件处理函数
void handleEvent() {
std::cout << "Handling event" << std::endl;
}
// 事件生产者线程函数
void producer() {
for (int i = 0; i < 5; ++i) {
std::unique_lock<std::mutex> lock(queueMutex);
eventQueue.push(handleEvent);
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
// 事件消费者线程函数
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(queueMutex);
if (!eventQueue.empty()) {
Event event = eventQueue.front();
eventQueue.pop();
lock.unlock();
event();
} else {
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
}
int main() {
std::thread producerThread(producer);
std::thread consumerThread(consumer);
producerThread.join();
consumerThread.join();
return 0;
}
在上述代码中,通过std::mutex
保护eventQueue
,避免竞态条件。生产者线程向队列中添加事件,消费者线程从队列中取出事件并处理,确保了事件驱动系统在多线程环境下的稳定性和正确性。