面试题答案
一键面试1. 使用 std::weak_ptr
解决循环引用面临的挑战
- 线程安全问题:
- 访问竞争:不同线程可能同时访问
std::weak_ptr
相关对象,比如一个线程正在将std::weak_ptr
提升为std::shared_ptr
,另一个线程可能正在销毁被指向的对象,导致未定义行为。 - 状态变更竞争:当一个线程通过
std::weak_ptr
提升为std::shared_ptr
时,另一个线程可能正在改变对象的生命周期状态(例如通过std::shared_ptr
释放对象),从而影响提升操作的结果。
- 访问竞争:不同线程可能同时访问
- 内存同步问题:
- 缓存一致性:现代处理器为提高性能,每个核心都有自己的缓存。不同线程对
std::weak_ptr
及其相关对象的读写操作可能因为缓存不一致而出现数据可见性问题。例如,一个线程更新了对象的引用计数(与std::weak_ptr
相关),但另一个线程可能由于缓存未及时更新,看不到这个变化。
- 缓存一致性:现代处理器为提高性能,每个核心都有自己的缓存。不同线程对
2. 相应解决方案
- 线程安全:
- 互斥锁保护:使用
std::mutex
对涉及std::weak_ptr
的关键操作(如提升、销毁对象等)进行加锁保护,确保同一时间只有一个线程可以执行这些操作。 - 原子操作:对于引用计数等关键的内存操作,使用原子类型(如
std::atomic
)来保证操作的原子性,避免数据竞争。
- 互斥锁保护:使用
- 内存同步:
- 使用
std::memory_order
:在涉及std::weak_ptr
及其相关对象的原子操作中,指定合适的std::memory_order
,如std::memory_order_release
和std::memory_order_acquire
,以确保内存操作的顺序性和可见性。
- 使用
3. 完整示例代码
#include <iostream>
#include <memory>
#include <thread>
#include <mutex>
#include <vector>
class B;
class A {
public:
std::weak_ptr<B> weakB;
~A() {
std::cout << "A destroyed" << std::endl;
}
};
class B {
public:
std::weak_ptr<A> weakA;
~B() {
std::cout << "B destroyed" << std::endl;
}
};
std::mutex mtx;
void threadFunction(std::shared_ptr<A>& a, std::shared_ptr<B>& b) {
{
std::lock_guard<std::mutex> lock(mtx);
a = std::make_shared<A>();
b = std::make_shared<B>();
a->weakB = b;
b->weakA = a;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(mtx);
if (auto tempB = a->weakB.lock()) {
std::cout << "A can access B through weak_ptr" << std::endl;
}
if (auto tempA = b->weakA.lock()) {
std::cout << "B can access A through weak_ptr" << std::endl;
}
}
}
int main() {
std::shared_ptr<A> a;
std::shared_ptr<B> b;
std::vector<std::thread> threads;
for (int i = 0; i < 2; ++i) {
threads.emplace_back(threadFunction, std::ref(a), std::ref(b));
}
for (auto& th : threads) {
th.join();
}
return 0;
}
在上述代码中:
- 互斥锁保护:使用
std::mutex
(mtx
)来保护对std::shared_ptr
和std::weak_ptr
的操作,确保线程安全。 - 对象创建与关联:在
threadFunction
中,通过std::make_shared
创建A
和B
的共享指针,并建立std::weak_ptr
的关联。 - 提升操作:在临界区内,通过
lock
方法将std::weak_ptr
提升为std::shared_ptr
,确保在提升过程中对象不会被其他线程销毁。