在多线程代码中使用shared_ptr
问题描述:
有一个示例代码在多线程环境中“工作”:在多线程代码中使用shared_ptr
void CSampleClass::Stop(void) {
if (m_pDB != nullptr) {
... Here is some code
m_pDB->Interrupt();
}
}
,其中
m_pDB
成员声明为
boost::shared_ptr<CSampleDatabase> m_pDB;
。
m_pDB
可以在另一个类的方法中重置。这就是为什么它被测试不是
nullptr
。由于代码具有多个线程,因此被另一个线程重置,在
if (...)
和
m_pDB->Interrupt();
之间时,可以是
情况。结果是相当戏剧性的。为了防止出现这种情况,我使用以下代码修改
void CSampleClass::Stop(void) {
auto pDb = m_pDB; //lock
if (pDb != nullptr) {
... Here is some code
pDb->Interrupt();
}
}
,即如果调用了
m_pDB.reset();
,则在销毁
pDb
之前,对象永远不会被释放。
的问题是:
是否有一个“标准”的方式,以防止情况不涉及储物柜,互斥,临界区等?像使用
boost::weak_ptr
来打破循环引用。确保编译器声明
pDB
为boost::shared_ptr<CSampleDatabase>
而不是CSampleDatabase *
?可能是写decltype(m_pDB) pDb = m_pDB; //lock
更安全吗?
答
提供的解决方案并不安全。它类似于这个例子从the documentation:
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
同时来自同一个实例读取是安全的。在同一个实例上的任何其他并发操作都不是。
有没有一种“标准”方式来防止不涉及储物柜,互斥体,临界区等情况?就像使用boost :: weak_ptr来打破循环引用一样。
您需要互斥,所以您需要使用互斥构造。
能够保证所有的编译器声明
pDB
为boost::shared_ptr<CSampleDatabase>
,而不是CSampleDatabase *
?可能是写decltype(m_pDB) pDb = m_pDB; //lock
更安全吗?
是的,pDB
将是shared_ptr
。
第二个版本仍然包含竞争条件。复制构建'shared_ptr'不是一个原子操作。 – Mankarse 2012-03-24 08:28:22
@Mancarse:引用? – 2012-03-24 08:49:54
@nm:从[here](http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm#ThreadSafety):“shared_ptr对象提供与内置相同级别的线程安全性类型”。这意味着多个'shared_ptr'实例引用同一对象并同时修改这些'shared_ptr'是安全的,但同时修改/读取一个'shared_ptr'实例并不安全。 – Mankarse 2012-03-24 09:05:59