shared_ptr 是线程安全的吗?
admin
2024-05-02 02:32:38
0次

这个话题真的很老了,不过今天的实验还是没有得出结论,所以就简单的记录一下实验过程吧,希望能够抛砖引玉,静待评论区的大神更深入的分析。

本文在实验过程中,主要参考了c++ 11 的shared_ptr多线程安全?里,果冻虾仁的回复。

我们先限制一下这里讨论的线程安全问题的范围:只考虑 shared_ptr 所管理的指针本身的创建和销毁过程是否线程安全,即 shared_ptr 所管理的自增和自减部分是否是安全的。至于指针所指向的类的自身构造和析构过程的线程安全,不属于讨论范围。

先 PO 完整的测试代码,再拆解来分析

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include using namespace std;void new_section(string section, string msg) {cout << endl;cout << "#################################################################" << endl;cout << "# [" << section << "] " << msg << endl;cout << "#################################################################" << endl;
}class Ref {
public:std::vector m_data;Ref(initializer_list l): m_data(l) {cout << "Ref(initializer_list l)" << endl;}string to_string() {stringstream ss;ss << "Ref(";bool first = true;for (auto v: m_data) {if (!first) { ss << ", "; }ss << v;}ss << ")";return ss.str();}
};shared_mutex gMutex;shared_ptr thread_func(shared_ptr& p) {  // demo according to https://www.zhihu.com/question/56836057cout << "ready" << endl;shared_lock lock(gMutex);  // comment this line out, see anything happenscout << "running" << endl;shared_ptr n{nullptr};n = p;p = nullptr;p = n;return n;
}int main()
{int nNumbers = 1000;int nThreads = 10000;int secsDelay = 5;list threads;{shared_ptr creator{new Ref{1,2,3}};for (int i = 0; i < nNumbers; ++i) {creator->m_data.push_back(i);}unique_lock lock(gMutex);for (int i = 0; i < nThreads; ++i) {threads.push_back(thread(bind(thread_func, std::ref(creator))));}for (int i = 0; i < secsDelay; ++i) {this_thread::sleep_for(chrono::seconds(1));cout << "counting " << i << endl;}}for (auto& t: threads) {t.join();}return 0;
}

首先,我们使用互斥量 gMutex 和锁 unique_lock 来完成操作的同时开始,类似于一个“起跑线”,让所有线程的 "n = 0; p = nullptr; p = n;" 操作,能够同时进行。


shared_mutex gMutex;shared_ptr thread_func(shared_ptr& p) {  // demo according to https://www.zhihu.com/question/56836057cout << "ready" << endl;// 关键行,让所有线程都在此处停留,用“读锁”保证“写锁”释放前,不会执行指针操作。//   同时保证,写锁释放后,所有线程能够并行的从这里开始执行(起跑)shared_lock lock(gMutex);cout << "running" << endl;shared_ptr n{nullptr};n = p;p = nullptr;p = n;return n;
}int main()
{int nNumbers = 1000;int nThreads = 10000;int secsDelay = 5;list threads;{shared_ptr creator{new Ref{1,2,3}};for (int i = 0; i < nNumbers; ++i) {creator->m_data.push_back(i);}// 用“写锁”,确保所有线程(包含“写锁”)不会执行,类似于画了一根“起跑线”unique_lock lock(gMutex);for (int i = 0; i < nThreads; ++i) {// 关键行,启动多个线程threads.push_back(thread(bind(thread_func, std::ref(creator)))); }for (int i = 0; i < secsDelay; ++i) {this_thread::sleep_for(chrono::seconds(1));cout << "counting " << i << endl;}// 利用锁的生命周期来解“写锁”,“写锁”接触后,所有线程即开始执行,类似于“起跑”}for (auto& t: threads) {t.join();}return 0;
}

本实验的测试结果,并未出现崩溃或者DEBUG报错。

但是从果冻虾仁的回复来看,最关键的,莫过于在 thread_func 里面,对入参 p 的两次赋值操作,即下面两行

    p = nullptr;p = n;

如果有异常情况,我的理解,应该是会有崩溃或者DEBUG的 Assert failuer 之类的错误。

所以从这个实验来看,应该还算是线程安全的。

当然,按照理论分析,问题确实看上去是有的,也可能是笔者的实验设定不能体现这个问题罢了。​​​​​​​

按照《c++ 11 的shared_ptr多线程安全?》里面其他回答来看,我们在跨线程的情况下,只要做到以下两点,应该就问题不大:

1. 传递指针的值

2. 不修改指针的值

那么如何修改这段代码呢?


shared_ptr thread_func(shared_ptr p) {  // @@@ 传指针的值cout << "ready" << endl;shared_lock lock(gMutex);cout << "running" << endl;shared_ptr n{p};// @@@ 删除原来的赋值操作return n;
}int main()
{int nNumbers = 1000;int nThreads = 10000;int secsDelay = 5;list threads;{shared_ptr creator{new Ref{1,2,3}};for (int i = 0; i < nNumbers; ++i) {creator->m_data.push_back(i);}unique_lock lock(gMutex);for (int i = 0; i < nThreads; ++i) {// @@@ 绑定时,也绑定值,而不是引用threads.push_back(thread(bind(thread_func, creator))); }for (int i = 0; i < secsDelay; ++i) {this_thread::sleep_for(chrono::seconds(1));cout << "counting " << i << endl;}}for (auto& t: threads) {t.join();}return 0;
}

相关内容

热门资讯

AI做攻略、“双向奔赴”……国... 本文转自:人民网人民网记者 许维娜今年国庆假期,AI(人工智能)成了不少年轻人的“旅行搭子”。行前做...
合肥的这个“梗”, 翻篇了 社交平台的评论区,曾长期流传一句专属于合肥的黑色幽默。有人问合肥哪里好玩,本地网友半开玩笑地回复:合...
7万+旅游中高层都在的群,今天... 做旅游这行,资源卡位、渠道互通、项目落地——往往就差一个“对的机会”。我们的行业社群已聚集70,00...
一场帐篷音乐节,如何让井研从“... 帐篷音乐节现场封面新闻记者 周洪攀10月的研溪湿地,晚风里裹着柑橘的清甜与民谣的回响。夜幕降临,10...
白鹤翩翩落星愿,上海迪士尼度假... 10月9日,世界候鸟日前夕,上海迪士尼度假区星愿公园的湖风里多了一分白鹤的灵动。三座白鹤雕塑正式揭幕...
成都锦江边赏初秋枫叶飘飘 目前,成都锦江边国华街枫叶树红了。近年来,国华街附近的几条街都栽种了红枫树,是城区较集中看枫叶的点位...
GBTA:商务旅行正在复苏,但... 亚太正在成为全球商务旅行增长的重要引擎。在经历疫情后的快速反弹之后,全球商务旅行正在进入一个新的增长...
从景区焕新到节赛引流 邹城双节... 齐鲁网·闪电新闻10月9日讯 中秋、国庆“双节”假期,邹城市文化和旅游局提前谋划、周密部署,立足自身...
江山如此多娇 且看石楼今朝 金秋十月,黄河岸畔秋意盎然,丰收盛会喜迎国庆。本次石楼国庆文旅活动以“江山如此多娇,且看石楼今朝”为...
海南国庆假期接待游客475.4... 2026年国庆假期,海南接待游客475.40万人次,较2025年增长13.1%,较2024年增长15...