0

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

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

3 回答 3

1

第 1 步:像这样构建一个类,并boost::shared_ptr<T>用它替换使用 of。

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变量上的线程安全是无法解决的。

于 2013-02-27T20:53:02.823 回答
1

您似乎遇到的问题是试图在两个单独的线程中修改同一个变量实例(又名数据竞争)。shared_ptr 没有比 int 更多的保护。您对 gcc 文档的引用说了同样的话(“与内置类型相同级别的线程安全”)。尝试在两个不同的线程中修改 shared_ptr 的同一个实例需要某种同步来防止数据竞争。尝试修改指向同一个对象的 shared_ptr 的两个不同实例是可以的(没有数据竞争,或者 shared_ptr 必须实现任何必要的东西来防止内部数据竞争)。试图修改它们指向的对象也是一种数据竞争。

于 2013-02-27T21:47:58.840 回答
0

std::shared_ptr 如果编译器/架构/实现支持/使用它,则可以使用原子整数。但我不会打赌,这样做会降低你的代码的可移植性。但它可以作为一种临时解决方法(例如,有一个正在运行的程序,以便您了解代码应该做什么)。

编写自己的shared_ptr包装器可能是一种选择,但您的代码仍需要进行死锁审核。

于 2013-02-27T19:37:13.290 回答