最近我开始研究一个遗留项目并尝试修复段错误(双删除)。其中许多发生在boost::shared_ptr
析构函数或operator=
(包含shared_ptr的对象)上。代码包含shared_ptr-s的大量使用,包括复制,重置(),分配等。根据boost docs我们没有有效的用法 - 在许多线程中破坏/复制/重置相同的shared_ptr是不安全的
每次锁定似乎都是不可能的,所以我正在寻找boost :: shared_ptr的替代品。所以问题是:如果我用boost::shared_ptr
或std::shared_ptr
替换所有std::tr1::shared_ptr
将解决此问题?似乎tr1是更安全的版本,但对我来说并不清楚。第二个问题 - c ++ 0x版本比tr1好吗? (注意我们有gcc 4.4.6并且无法升级它)
根据gcc docs,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 exit\n";
return 0;
}
答案 0 :(得分:1)
步骤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
变量上的线程安全性无法修复的方式打破的。
答案 1 :(得分:1)
您遇到的问题是尝试在两个单独的线程中修改变量的相同实例(AKA数据竞争)。 shared_ptr不会比int更受到保护。您对gcc文档的引用说明了同样的事情(&#34;与内置类型相同的线程安全级别&#34;)。尝试在两个不同的线程中修改shared_ptr的同一实例需要某种同步来防止数据竞争。尝试修改指向同一对象的shared_ptr的两个不同实例是可以的(没有数据竞争,或者shared_ptr必须实现防止内部数据竞争所需的任何内容)。试图修改他们指向的对象也是数据竞争。
答案 2 :(得分:0)
std::shared_ptr
可能使用原子int。但我不会赌它,这样做会使你的代码不那么便携。但它可以作为临时解决方法(例如,有一个正在运行的程序,以便您了解代码应该做什么)。
编写自己的shared_ptr
包装器可能是一个选项,但仍然需要审核您的代码是否存在死锁。