面试题答案
一键面试线程安全问题及解决方法
- 竞态条件
- 问题描述:多个线程同时访问和修改共享资源,导致最终结果取决于线程执行的顺序。例如,多个线程同时对一个计数器变量进行自增操作,可能会出现数据不一致的情况。
- 解决方法:
- 互斥锁(
std::mutex
):使用std::mutex
来保护共享资源。在访问共享资源前锁定互斥锁,访问完成后解锁。例如:
- 互斥锁(
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mtx;
int counter = 0;
void increment() {
mtx.lock();
counter++;
mtx.unlock();
}
- **读写锁(`std::shared_mutex`)**:当读操作远多于写操作时,可以使用读写锁。多个线程可以同时进行读操作,但写操作时需要独占锁。例如:
#include <iostream>
#include <shared_mutex>
#include <thread>
std::shared_mutex rw_mtx;
int data = 0;
void read() {
rw_mtx.lock_shared();
std::cout << "Read data: " << data << std::endl;
rw_mtx.unlock_shared();
}
void write() {
rw_mtx.lock();
data++;
rw_mtx.unlock();
}
- 死锁
- 问题描述:两个或多个线程相互等待对方释放资源,导致所有线程都无法继续执行。例如,线程A持有锁1并等待锁2,而线程B持有锁2并等待锁1。
- 解决方法:
- 避免嵌套锁:尽量避免在一个线程中获取多个锁,若必须获取多个锁,确保所有线程以相同的顺序获取锁。
- 使用
std::lock
:std::lock
函数可以一次性获取多个锁,并且不会产生死锁。例如:
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mutex1;
std::mutex mutex2;
void threadFunction() {
std::lock(mutex1, mutex2);
std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
// 执行需要两个锁保护的操作
}
资源管理避免内存泄漏
- 智能指针
- 问题描述:在异步调用中,如果动态分配的内存没有正确释放,可能会导致内存泄漏。例如,在一个线程中分配了内存,但是线程异常终止或者没有正确释放内存。
- 解决方法:使用智能指针(
std::unique_ptr
、std::shared_ptr
)。std::unique_ptr
拥有对象的唯一所有权,当std::unique_ptr
离开作用域时,对象会自动销毁。std::shared_ptr
允许多个指针共享对象的所有权,当最后一个指向对象的std::shared_ptr
销毁时,对象会自动销毁。例如:
#include <iostream>
#include <memory>
#include <thread>
void processData(std::unique_ptr<int> data) {
// 处理数据
std::cout << "Processing data: " << *data << std::endl;
}
int main() {
auto data = std::make_unique<int>(42);
std::thread t(processData, std::move(data));
t.join();
return 0;
}
复杂示例代码
#include <iostream>
#include <mutex>
#include <thread>
#include <functional>
#include <memory>
class SharedResource {
public:
SharedResource(int value) : data(value) {}
void updateData(int newVal) {
std::lock_guard<std::mutex> lock(mtx);
data = newVal;
}
int getData() const {
std::lock_guard<std::mutex> lock(mtx);
return data;
}
private:
int data;
mutable std::mutex mtx;
};
class CallbackHandler {
public:
CallbackHandler() : resource(std::make_shared<SharedResource>(0)) {}
void asyncCallback() {
std::thread([this]() {
for (int i = 0; i < 5; ++i) {
resource->updateData(i);
std::cout << "Thread " << std::this_thread::get_id() << " updated data to " << resource->getData() << std::endl;
}
}).detach();
}
private:
std::shared_ptr<SharedResource> resource;
};
int main() {
CallbackHandler handler;
handler.asyncCallback();
// 主线程可以继续执行其他操作
for (int i = 0; i < 3; ++i) {
std::cout << "Main thread doing other work " << i << std::endl;
}
// 主线程退出前可以等待一段时间,确保异步线程完成操作
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}
在这个示例中:
SharedResource
类使用std::mutex
来保护成员变量data
,以避免竞态条件。CallbackHandler
类使用std::shared_ptr
来管理SharedResource
对象,确保资源在不再需要时正确释放。asyncCallback
方法使用std::thread
启动一个异步线程,并通过std::function
传递回调函数。在回调函数中,线程安全地更新和读取共享资源。