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
    image

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,进行读写操作,并非线程安全

4.参考

std::shared_ptr的线程安全性
constexpr 1
constexpr 2

posted @ 2024-05-26 23:47  胖白白  阅读(268)  评论(0编辑  收藏  举报