2013-02-27 68 views
0

最近我开始在一个传统项目上工作,并试图修复段错误(双删除)。它们中的很多正在发生在boost::shared_ptr析构函数或operator=(在包含shared_ptr的对象上)。代码包含shared_ptr-s的大量使用,包括复制,重置(),分配等。根据boost docs,我们没有有效的用法 - 它在多个线程中不能安全地销毁/复制/重置相同的shared_ptr。boost :: shared_ptr drop-in replacement

每次锁定似乎都不可能,所以即时搜索boost :: shared_ptr的插入替换。所以问题是:如果我用std::shared_ptrstd::tr1::shared_ptr替换所有boost::shared_ptr将解决此问题?看起来tr1是更安全的版本,但对我来说并不明确。第二个问题 - C++ 0x版本比tr1更好吗? (注意,我们有GCC 4.4.6,并且不能升级)

Accoring到gcc docs,C++ 11的std :: shared_ptr的应该解决这个问题了,但是我不知道GCC4.4版本...

UPD :只要maked实验,现在我知道所有的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

boost :: shared_ptr非常稳定。我不认为用另一种实现替换它会“解决”任何事情。如果你看到两次删除,那么你会继续看到这些,直到你找到并修复你的错误。 – Bukes 2013-02-27 19:28:03

+4

“每次锁定似乎都不可能”表示您有线程问题。改变智能指针的实现并不会为你购买任何东西。 boost :: shared_ptr不是越野车。 – 2013-02-27 19:28:59

+0

你在寻找一个线程安全的共享指针吗? – 2013-02-27 19:31:35

回答

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变量的线程安全性不会解决。

+0

感谢您的回复。也许我应该切换到自定义类..我的实验显示所有3个标准实现失败此用例( – PSIAlt 2013-02-27 21:06:47

+0

@ PSIAlt:你没有读过他的最后一段吗?你不能通过创建一个新的指针类型来解决问题。除非你将要互斥 - 包装每一个指针实例,即使如此,你仍然会遇到问题,因为有人会认为旧指针是在附近。你无法逃避你已经交给了一个可怕的错误代码库的事实,而你需要解决它。 – 2013-02-27 21:33:23

+0

@NicolBolas唉,看来真的有比没有别的办法..不知道又是什么。它看起来像我没有机会没有经纪东西改写这一点。但是,感谢指点一下锁定 – PSIAlt 2013-02-27 21:42:18

0

std::shared_ptr可能使用原子整数,如果编译器/体系结构/实现支持/使用它。但我不会赌它,这样做会使你的代码更加便携。但它可能只是一个临时的解决方法(例如,让一个正在运行的程序,以便您了解代码应该执行的操作)。

编写你自己的shared_ptr包装可能是一个选项,但你的代码仍然需要审核死锁。

+3

'std :: shared_ptr'必须是线程安全的......但*仅*作为线程安全的'boost: :shared_ptr'。也就是说,两个不同的shared_ptr对象可以引用相同的数据,并且删除会很好。但是你不能在两个不同的线程中查找* same * shared_ptr对象。后者是OP的问题。 – 2013-02-27 21:31:13

+0

'shared_ptr' **是必需**为线程安全的,但仅用于同时复制实例,不能同时修改它们 – 2013-02-28 18:13:23

1

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

相关问题