C++中shared_ptr线程安全问题与constexpr关键字作用
1.shared_ptr线程安全问题
1.1.多线程多对象指向一个shared_ptr导致的线程不安全
- 例子:创建了10个线程,每个线程调用 ThreadFunc()函数。ThreadFunc()函数将g_instance std::shared_ptr对象的值增加10000次。main()函数然后休眠5000毫秒,这给了线程执行的时间。线程执行完毕后,main())函数将打印g_instance(std::shared_ptr对象)的值
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
#include <vector>
using namespace std;
std::mutex g_mutex;
std::shared_ptr<int> g_instance = std::make_shared<int>(0);
constexpr int maxLoop = 10000;
void ThreadFunc()
{
for (int i = 0; i < maxLoop; i++) {
std::shared_ptr<int> tmp = g_instance;
(*tmp)++;
}
cout << "g_instance use count : " << g_instance.use_count() << endl;
}
int main()
{
*g_instance = 0;
vector<std::thread> vc;
for (int i = 0; i < 10; i++) {
vc.push_back(std::thread(ThreadFunc));
}
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
for (auto &t : vc) {
t.join();
}
cout << __FUNCTION__ << "-> g_instance : " << *g_instance << endl;
return 0;
}
- 输出,发现g_instance并没有预想的加到10000
1.2.加锁解决
在上面的程序加一行代码std::lock_guard<std::mutex> lock(g_mutex);
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
#include <vector>
using namespace std;
std::mutex g_mutex;
std::shared_ptr<int> g_instance = std::make_shared<int>(0);
constexpr int maxLoop = 10000;
void ThreadFunc()
{
for (int i = 0; i < maxLoop; i++) {
std::shared_ptr<int> tmp = g_instance;
////////////////////////////////// 加锁
std::lock_guard<std::mutex> lock(g_mutex);
(*tmp)++;
}
cout << "g_instance use count : " << g_instance.use_count() << endl;
}
int main()
{
*g_instance = 0;
vector<std::thread> vc;
for (int i = 0; i < 10; i++) {
vc.push_back(std::thread(ThreadFunc));
}
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
for (auto &t : vc) {
t.join();
}
cout << __FUNCTION__ << "-> g_instance : " << *g_instance << endl;
return 0;
}
- 输出
2.tips: constexpr的作用
constexpr是一种在编译时求值的机制,它不仅限于变量的声明,还可以用于函数、表达式等,旨在提高代码的性能、安全性和易读性
具体用法见上文和下面的参考
3.结论
- shared_ptr本身的引用计数是原子操作,线程安全
- 但是在多线程环境下,多个对象指向同一个shared_ptr,进行读写操作,并非线程安全