Boost::shared_ptr插入式替换
boost::shared_ptr drop-in replacement
最近我开始在一个遗留项目上工作,并试图修复分段错误(双重删除)。其中许多都发生在boost::shared_ptr
析构函数或operator=
上(在包含shared_ptr的对象上)。代码包含大量使用shared_ptr-s,包括复制、reset()-ing、赋值等。根据boost文档,我们没有有效的使用-在多个线程中销毁/复制/重置同一个shared_ptr是不安全的。
每次锁定似乎是不可能的,所以我正在寻找boost::shared_ptr的插入式替代品。所以问题是:如果我用std::shared_ptr
或std::tr1::shared_ptr
代替所有的boost::shared_ptr
,会解决这个问题吗?似乎tr1是更安全的版本,但我不清楚。第二个问题- c++0x版本比tr1更好吗?(注意我们有GCC 4.4.6,不能升级)
根据gcc文档,c++11 std::shared_ptr应该修复这个问题,但我不确定gcc4.4版本…
UPD:刚刚做了一个实验,现在我知道所有3个实现都在这个代码(gcc 4.4)上做段错误。似乎我应该自定义类或其他解决方案…
#include <iostream>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<int> ptrtype;
ptrtype p(new int);
void test() {
for(long i=0; i<1000000; ++i) {
ptrtype p1 = p;
p = ptrtype();
p.reset( new int );
}
}
int main() {
boost::thread_group tg;
for(int i=0; i<100; ++i) tg.add_thread( new boost::thread(test) );
tg.join_all();
std::cout << "Normal exitn";
return 0;
}
步骤1:像这样构建一个类,并用它替换boost::shared_ptr<T>
的使用。
template<typename T>
struct trivial_ptr {
T* t;
template<typename U>
void reset( U* p ) {t=p;}
void reset( T* p = NULL ) { t=p; }
template<typename U>
trivial_ptr<T>& operator=(trivial_shared_ptr<U>const& o) {
t = o.t;
return *t;
}
explicit trivial_ptr( T* p ):t(p) {}
...
};
这个类不是用来运行的,而只是用正确的接口进行编译。编译完成后,您可以确保知道正在使用的是boost::shared_ptr
接口的哪些部分。(您是否使用自定义删除程序?等等——问题可能更困难,也可能更容易,上面的代码可以帮助测试它)
一旦你到了那里,你就可以知道编写一个shared_ptr<T>
处理多个线程同时访问同一个变量是多么困难。
这是非常混乱的。如果一个线程reset
是给定的shared_ptr
,而另一个线程从它读取,那么当读取线程访问它时,从读取的指针可能完全无效。实际上,您需要保护所有对互斥锁中底层指针的访问,这是完全不切实际的。
另一方面,如果您有多个读取器,而不是一个读取器和一个写入器,则情况会更好——理论上,您可以通过在引用计数代码上使用适当的锁来解决这个问题。
然而,你实际描述的似乎涉及多个线程对同一个变量的读写。这在某种程度上是根本的,仅仅是shared_ptr
变量上的线程安全是无法修复的。
您似乎遇到的问题是试图在两个不同的线程中修改变量的相同实例(又称数据竞争)。Shared_ptr并不比int型更受保护。您对gcc文档的参考说明了同样的事情("与内置类型相同级别的线程安全")。试图在两个不同的线程中修改同一个shared_ptr实例需要某种同步来防止数据争用。尝试修改指向同一对象的shared_ptr的两个不同实例是可以的(没有数据竞争,或者shared_ptr必须在内部实现防止数据竞争所必需的任何东西)。试图修改它们所指向的对象也是一种数据竞争。
std::shared_ptr
可以使用原子整型,如果编译器/体系结构/实现支持的话。但我不会这么做,因为这样做会降低代码的可移植性。但它可以作为一个临时的解决方案(例如,有一个正在运行的程序,这样你就可以理解代码应该做什么)。
编写自己的shared_ptr
包装器可能是一个选项,但是您的代码仍然需要审计死锁。
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 模板参数替换失败,并且未完成隐式转换
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 预处理器:插入结构名称中的前一个行号
- 在未初始化映射的情况下,将值插入到映射的映射中
- 如何用转义符替换字符串中的所有特殊字符
- 如何在c++中只将键插入到bimap的一侧
- 如何将结构插入到集合中并打印集合的成员
- C++json插入数组
- Visual Studio 2019:插入多个C++风格的单行注释
- 地图插入将替换以前输入的值
- 插入到二叉搜索树中.节点替换
- QtLineEdit 占位符:如何让用户在插入模式下将字符 1 替换为 1
- C sqlite3插入或替换查询我认为是错误的
- 将元素插入向量并替换所有其他元素的函数,从而截断最终元素
- 替换std::transform,插入到std::vector中
- 插入和替换代价不均匀的Levenshtein距离:
- Boost::shared_ptr插入式替换
- 列表元素被替换而不是插入
- sql语句插入或替换从java到c++ NDK Android