2013-02-27 3 views
0

최근에 저는 하나의 레거시 프로젝트에서 작업하고 segfaults (이중 삭제)를 수정하려고했습니다. 많은 사람들이 boost::shared_ptr 소멸자 또는 (shared_ptr을 포함하는 객체)에서 발생합니다. 이 코드에는 복사, 재설정(), 할당 등을 포함한 shared_ptr-s의 방대한 사용이 포함되어 있습니다. boost docs에 따르면 유효한 사용법이 없습니다. 많은 스레드에서 동일한 shared_ptr을 파괴/복사/재설정하는 것이 안전하지 않습니다.boost :: shared_ptr 드롭 인 대체

매번 잠금 기능을 사용할 수 없으므로 boost :: shared_ptr 대신 drop-in replacement를 검색합니다. 질문 : 모든 boost::shared_ptrstd::shared_ptr 또는 std::tr1::shared_ptr으로 바꿀 경우이 문제가 해결됩니까? Tr1이 더 안전한 버전이라고 생각되지만 그 점은 분명하지 않습니다. 두 번째 질문 - tr1보다 C++ 0x 버전이 더 나은가요? ++ 11 표준 : : shared_ptr의 그 문제를 해결하지만, gcc4.4 버전에 대해 확실하지 메신저해야 c를 ...,

gcc docs에 accoring에

UPD를 (우리가 GCC 4.4.6을하고 업그레이드 할 수 있습니다) : 그냥

#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

+4

"매번 잠금이 불가능한 것 같습니다"는 것은 스레딩 문제가 있음을 나타냅니다. 스마트 포인터 구현을 변경하면 아무 것도 사지 않을 것입니다. boost :: shared_ptr은 버그가 없습니다. –

+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의 단순한 스레드 안전성이 수정되지 않는 방식으로 근본적으로 부러졌습니다.

+0

답장을 보내 주셔서 감사합니다. 아마 내가 사용자 지정 클래스로 전환해야합니다. 내 모든 실험을 3 개의 표준 구현을 보여주는이 사용 사례가 실패합니다 ( – PSIAlt

+0

@ PSIAlt : 마지막 단락을 읽지 않았습니까? 새 포인터 유형을 만들어 문제를 해결할 수 없습니다. 뮤텍스로 모든 포인터 인스턴스를 감싸려고 할 때, 심지어 누군가는 이전 포인터가 있다고 가정 할 것이기 때문에 여전히 문제가있을 것입니다. 당신이 끔찍한 버그가있는 코드베이스를 건네 졌다는 사실을 피할 수는 없습니다. –

+0

@NicolBolas 우, 정말 다른 방법이없는 것 같습니다 .. 아직 모르겠군요. 뭔가를 중재하지 않고 다시 작성할 기회가없는 것처럼 보입니다. 그러나 잠금에 대해 지적 해 주셔서 고마워요. – PSIAlt

0

std::shared_ptr수도 사용 원자의 int 만약 ... 내가 3 구현이 코드 (GCC 4.4) ... 난 아마 사용자 정의 클래스 또는 다른 해결 방법을 해봐야 할 것 같습니다에 세그 폴트 거 알아 지금 실험을 표시했습니다 및 컴파일러/아키텍처/구현은이를 지원/사용합니다. 그러나 나는 그것에 대해 내기를하지 않을 것이고 그렇게하면 코드의 이식성이 떨어진다. 그러나 임시 해결 방법으로 작동 할 수 있습니다 (예 : 코드 실행 내용을 이해할 수 있도록 실행중인 프로그램이있는 경우).

독자적인 shared_ptr 래퍼를 작성하는 것도 옵션 일 수 있지만 코드는 교착 상태에 대해 감사해야합니다.

+3

'std :: shared_ptr'은 thread-safe가되어야 합니다만, 부스트로서 안전합니다 : : shared_ptr'. 즉, 두 개의 다른 shared_ptr 객체가 동일한 데이터를 참조 할 수 있으며 삭제가 문제가되지 않습니다. 하지만 두 개의 다른 스레드에서 같은 * shared_ptr 객체를 찌를 수는 없습니다. 후자는 OP가 가지고있는 문제입니다. –

+0

'shared_ptr'은 ** 스레드로부터 안전하지만 인스턴스를 동시에 복사 할 때만 필요하며 ** 동시에 수정할 필요는 없습니다. –

1

문제는 두 개의 개별 스레드 (일명 데이터 경주)에서 변수의 동일한 인스턴스를 수정하려고 시도하는 것입니다. shared_ptr는 int의 것보다 더 이상 보호되지 않습니다. gcc 문서에 대한 참조는 같은 것을 말합니다 ("기본 제공 유형과 동일한 수준의 스레드 안전성"). 두 개의 다른 스레드에서 shared_ptr의 동일한 인스턴스를 수정하려고하면 데이터 경합을 막기 위해 일종의 동기화가 필요합니다. 동일한 객체를 가리키는 shared_ptr의 두 인스턴스를 수정하려고하면 (데이터 경쟁이 없거나 shared_ptr이 데이터 경쟁을 방지하는 데 필요한 모든 것을 구현해야합니다). 그들이 가리키는 개체를 수정하려고 시도하는 것은 데이터 경주이기도합니다.

관련 문제