boost :: shared_ptr drop-in替换

时间:2013-02-27 19:24:20

标签: c++ boost c++11

最近我开始研究一个遗留项目并尝试修复段错误(双删除)。其中许多发生在boost::shared_ptr析构函数或operator=(包含shared_ptr的对象)上。代码包含shared_ptr-s的大量使用,包括复制,重置(),分配等。根据boost docs我们没有有效的用法 - 在许多线程中破坏/复制/重置相同的shared_ptr是不安全的

每次锁定似乎都是不可能的,所以我正在寻找boost :: shared_ptr的替代品。所以问题是:如果我用boost::shared_ptrstd::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;
}

3 个答案:

答案 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包装器可能是一个选项,但仍然需要审核您的代码是否存在死锁。