可能遇到的线程安全问题
- 引用计数更新问题:多个线程同时访问和修改
std::shared_ptr
的引用计数时,可能会导致数据竞争。例如,一个线程正在增加引用计数,另一个线程同时减少引用计数,这可能会导致引用计数出现不一致的情况,最终可能导致对象被提前释放或内存泄漏。
- 对象访问问题:当一个线程正在访问
std::shared_ptr
指向的对象,而另一个线程释放了该对象(通过将 std::shared_ptr
的引用计数减为 0),那么访问对象的线程可能会访问到已释放的内存,导致未定义行为。
解决方法
- 使用互斥锁(Mutex):
- 原理:通过互斥锁来保护对
std::shared_ptr
的操作,确保同一时间只有一个线程可以访问和修改 std::shared_ptr
。
- 示例代码:
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
std::mutex mtx;
std::shared_ptr<int> sharedData;
void threadFunction() {
std::lock_guard<std::mutex> lock(mtx);
if (!sharedData) {
sharedData = std::make_shared<int>(42);
}
std::cout << "Thread sees value: " << *sharedData << std::endl;
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
- 使用
std::atomic_shared_ptr
(C++20 及以上):
- 原理:
std::atomic_shared_ptr
提供了原子操作语义,确保对 std::shared_ptr
的各种操作(如赋值、比较、释放等)是线程安全的,无需额外的锁。
- 示例代码:
#include <iostream>
#include <memory>
#include <atomic>
#include <thread>
std::atomic_shared_ptr<int> atomicSharedData;
void threadFunction() {
auto localPtr = atomicSharedData.load();
if (!localPtr) {
localPtr = std::make_shared<int>(42);
atomicSharedData.store(localPtr);
}
std::cout << "Thread sees value: " << *localPtr << std::endl;
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}
- 使用线程本地存储(Thread - Local Storage,TLS):
- 原理:每个线程都有自己独立的
std::shared_ptr
副本,避免了多个线程对同一个 std::shared_ptr
的竞争。但是,这种方法适用于每个线程需要独立处理对象的场景,不适合线程间需要共享对象状态的情况。
- 示例代码:
#include <iostream>
#include <memory>
#include <thread>
#include <thread>
thread_local std::shared_ptr<int> localSharedData;
void threadFunction() {
if (!localSharedData) {
localSharedData = std::make_shared<int>(42);
}
std::cout << "Thread sees value: " << *localSharedData << std::endl;
}
int main() {
std::thread t1(threadFunction);
std::thread t2(threadFunction);
t1.join();
t2.join();
return 0;
}