面试题答案
一键面试线程安全问题
- 数据竞争:当多个线程同时访问和修改
std::vector<std::unique_ptr<MyClass>>
时,可能会发生数据竞争。例如,一个线程在读取元素,另一个线程在删除或插入元素,这可能导致未定义行为。 - 竞态条件:如果对数据结构的操作依赖于特定的执行顺序,例如先检查元素是否存在再进行操作,多个线程同时执行这些操作可能导致竞态条件。
内存管理问题
- 悬空指针:如果一个线程释放了
MyClass
对象的内存(通过std::unique_ptr
析构),而另一个线程仍持有指向该对象的指针,就会产生悬空指针。 - 双重释放:虽然
std::unique_ptr
本身可以避免双重释放,但在多线程环境下,如果不小心手动释放了内存,然后std::unique_ptr
又进行析构,就会导致双重释放。
利用常指针和指向常变量的指针特性保证安全
- 常指针(
MyClass* const
):常指针本身不能被重新赋值,但指向的对象可以被修改。这在多线程环境下可以确保指针不会被意外修改,但不能防止对象内容被并发修改。 - 指向常变量的指针(
const MyClass*
):指向常变量的指针不能修改所指向对象的内容。这在多线程环境下可以保证只读操作的线程安全,只要没有线程进行写操作。
关键代码示例
#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}
};
std::vector<std::unique_ptr<MyClass>> data;
std::mutex dataMutex;
// 线程安全的只读访问
void readData() {
std::lock_guard<std::mutex> lock(dataMutex);
for (const auto& ptr : data) {
const MyClass* const obj = ptr.get();
std::cout << "Read value: " << obj->value << std::endl;
}
}
// 线程安全的写访问
void writeData() {
std::lock_guard<std::mutex> lock(dataMutex);
data.emplace_back(std::make_unique<MyClass>(42));
}
代码解释
std::mutex dataMutex
:用于保护data
数据结构,防止多线程同时访问和修改。readData
函数:使用std::lock_guard
来锁定互斥锁,确保在读取数据时不会有其他线程修改数据。const MyClass* const obj = ptr.get();
获取指向MyClass
对象的常指针,保证只读访问。writeData
函数:同样使用std::lock_guard
锁定互斥锁,确保在插入新元素时不会有其他线程干扰。
通过使用互斥锁和合理利用常指针和指向常变量的指针特性,可以有效保证多线程环境下 std::vector<std::unique_ptr<MyClass>>
数据结构的一致性和安全性。