2010-06-14 3 views
7

Valgrind --tool = drd를 사용하여 Boost :: thread를 사용하는 응용 프로그램을 검사하고 있습니다. 기본적으로 응용 프로그램은 소켓 연결을 통한 입력을 기반으로 "Book"값 세트를 "Kehai"값으로 채 웁니다.경쟁 조건이 발생하는 위치를 파악할 수 없습니다.

별도의 스레드에서 사용자는 연결하여 책을 보낼 수 있습니다.

매우 간단합니다. 책을 직렬화하는 위치에 boost :: mutex :: scoped_lock을 사용하여 책 데이터를 지우는 위치는 경쟁 조건을 방지하는 데 충분해야합니다. datagetting 스레드 각각의 스레드를 송신 데이터라고

void Book::clear() 
    { 
    boost::mutex::scoped_lock lock(dataMutex); 
    for(int i =NUM_KEHAI-1; i >= 0; --i) 
    { 
     bid[i].clear(); 

     ask[i].clear(); 
    } 
    } 

    int Book::copyChangedKehaiToString(char* dst) const 
    { 
    boost::mutex::scoped_lock lock(dataMutex); 

    sprintf(dst, "%-4s%-13s",market.c_str(),meigara.c_str()); 
    int loc = 17; 
    for(int i = 0; i < Book::NUM_KEHAI; ++i) 
    { 
     if(ask[i].changed > 0) 
     { 
     sprintf(dst+loc,"A%i%-21s%-21s%-21s%-8s%-4s",i,ask[i].price.c_str(),ask[i].volume.c_str(),ask[i].number.c_str(),ask[i].postTime.c_str(),ask[i].status.c_str()); 
     loc += 77; 
     } 
    } 
    for(int i = 0; i < Book::NUM_KEHAI; ++i) 
    { 
     if(bid[i].changed > 0) 
     { 
     sprintf(dst+loc,"B%i%-21s%-21s%-21s%-8s%-4s",i,bid[i].price.c_str(),bid[i].volume.c_str(),bid[i].number.c_str(),bid[i].postTime.c_str(),bid[i].status.c_str()); 
     loc += 77; 
     } 
    } 

    return loc; 
    } 

클리어() 함수와 copyChangedKehaiToString() 함수 다음은 코드이다. 또한 , 메모 등, 클래스 도서 :

struct Book 
    { 
    private: 
    Book(const Book&); Book& operator=(const Book&); 
    public: 

    static const int NUM_KEHAI=10; 
    struct Kehai; 
    friend struct Book::Kehai; 

    struct Kehai 
    { 
    private: 
     Kehai& operator=(const Kehai&); 
    public: 
     std::string price; 
     std::string volume; 
     std::string number; 
     std::string postTime; 
     std::string status; 

     int changed; 
     Kehai(); 
     void copyFrom(const Kehai& other); 
     Kehai(const Kehai& other); 
     inline void clear() 
     { 

     price.assign(""); 
     volume.assign(""); 
     number.assign(""); 
     postTime.assign(""); 
     status.assign(""); 
     changed = -1; 
     } 
    }; 

    std::vector<Kehai> bid; 
    std::vector<Kehai> ask; 
    tm recTime; 
    mutable boost::mutex dataMutex; 


    Book(); 
    void clear(); 
    int copyChangedKehaiToString(char * dst) const; 
     }; 

사용 Valgrind의 --tool = DRD, 나는 그런 아래와 같이 경쟁 조건 오류를 얻을 :

==26330== Conflicting store by thread 1 at 0x0658fbb0 size 4 
==26330== at 0x653AE68: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (in /usr/lib/libstdc++.so.6.0.8) 
==26330== by 0x653AFC9: std::string::_M_replace_safe(unsigned int, unsigned int, char const*, unsigned int) (in /usr/lib/libstdc++.so.6.0.8) 
==26330== by 0x653B064: std::string::assign(char const*, unsigned int) (in /usr/lib/libstdc++.so.6.0.8) 
==26330== by 0x653B134: std::string::assign(char const*) (in /usr/lib/libstdc++.so.6.0.8) 
==26330== by 0x8055D64: Book::Kehai::clear() (Book.h:50) 
==26330== by 0x8094A29: Book::clear() (Book.cpp:78) 
==26330== by 0x808537E: RealKernel::start() (RealKernel.cpp:86) 
==26330== by 0x804D15A: main (main.cpp:164) 
==26330== Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8 
==26330== Other segment start (thread 2) 
==26330== at 0x400BB59: pthread_mutex_unlock (drd_pthread_intercepts.c:633) 
==26330== by 0xC59565: pthread_mutex_unlock (in /lib/libc-2.5.so) 
==26330== by 0x805477C: boost::mutex::unlock() (mutex.hpp:56) 
==26330== by 0x80547C9: boost::unique_lock<boost::mutex>::~unique_lock() (locks.hpp:340) 
==26330== by 0x80949BA: Book::copyChangedKehaiToString(char*) const (Book.cpp:134) 
==26330== by 0x80937EE: BookSerializer::serializeBook(Book const&, std::string const&) (BookSerializer.cpp:41) 
==26330== by 0x8092D05: BookSnapshotManager::getSnaphotDataList() (BookSnapshotManager.cpp:72) 
==26330== by 0x8088179: SnapshotServer::getDataList() (SnapshotServer.cpp:246) 
==26330== by 0x808870F: SnapshotServer::run() (SnapshotServer.cpp:183) 
==26330== by 0x808BAF5: boost::_mfi::mf0<void, RealThread>::operator()(RealThread*) const (mem_fn_template.hpp:49) 
==26330== by 0x808BB4D: void boost::_bi::list1<boost::_bi::value<RealThread*> >::operator()<boost::_mfi::mf0<void, RealThread>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, RealThread>&, boost::_bi::list0&, int) (bind.hpp:253) 
==26330== by 0x808BB90: boost::_bi::bind_t<void, boost::_mfi::mf0<void, RealThread>, boost::_bi::list1<boost::_bi::value<RealThread*> > >::operator()() (bind_template.hpp:20) 
==26330== Other segment end (thread 2) 
==26330== at 0x400B62A: pthread_mutex_lock (drd_pthread_intercepts.c:580) 
==26330== by 0xC59535: pthread_mutex_lock (in /lib/libc-2.5.so) 
==26330== by 0x80546B8: boost::mutex::lock() (mutex.hpp:51) 
==26330== by 0x805473B: boost::unique_lock<boost::mutex>::lock() (locks.hpp:349) 
==26330== by 0x8054769: boost::unique_lock<boost::mutex>::unique_lock(boost::mutex&) (locks.hpp:227) 
==26330== by 0x8094711: Book::copyChangedKehaiToString(char*) const (Book.cpp:113) 
==26330== by 0x80937EE: BookSerializer::serializeBook(Book const&, std::string const&) (BookSerializer.cpp:41) 
==26330== by 0x808870F: SnapshotServer::run() (SnapshotServer.cpp:183) 
==26330== by 0x808BAF5: boost::_mfi::mf0<void, RealThread>::operator()(RealThread*) const (mem_fn_template.hpp:49) 
==26330== by 0x808BB4D: void boost::_bi::list1<boost::_bi::value<RealThread*> >::operator()<boost::_mfi::mf0<void, RealThread>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, RealThread>&, boost::_bi::list0&, int) (bind.hpp:253) 

내 인생 , 나는 경쟁 조건이 어디에 있는지 알 수 없다. 내가 말할 수있는 한, kehai를 지우는 것은 뮤텍스를 취한 후에 만 ​​이루어지며, 똑같은 것은 문자열에 복사하는 것과 마찬가지입니다. 누군가이 문제의 원인이 될 수있는 아이디어가 있습니까?

감사합니다.

답변

6

게시물을 작성한 후 Valgrind에 대한 정보를 얻고 그 결과를 읽어야합니다.

당신은 회전 당신이 문자열에 값을 지정 Book::Kehai::clear를 호출 Book::clear를 호출 :

나는 다음을 참조 할 수 있습니다. std::string::assign 내부의 STL은 0x0658fbb0 주소에 값을 저장합니다.

한편 다른 스레드는 동일한 메모리 위치에 액세스하므로이 상황은 경쟁 조건으로 간주됩니다.

이제 다른 스레드의 "컨텍스트"를 살펴보십시오. Valgrind는 정확한 스택 위치를 표시하지 않지만 발생 된 "세그먼트"를 보여줍니다. Valgrind에 따르면 세그먼트는 동기화 작업으로 묶인 메모리 액세스의 연속 블록입니다.

우리는 pthread_mutex_unlock을 시작 이 블록은 pthread_mutex_lock에서 끝나는 것을 알 수있다. 의미 - 뮤텍스가 잠겨 있지 않은 상태에서 동일한 메모리 위치에 액세스했으며 해당 스레드가 두 기능 외부에 있었던 것입니다.

이제 충돌하는 메모리 위치 정보를 보면 :

Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8 

방송 일정이 글로벌/정적 변수의 것을 의미한다. 그리고 그것은 libstdc의 어딘가에 정의되어 있습니다.

결론 :

이 경쟁 조건은 데이터 구조와 관련이 없습니다. STL과 관련이 있습니다. 한 스레드는 std::string (뭔가 정확히 빈 문자열에 할당)에 뭔가를 수행하는 반면, 다른 스레드는 아마도 STL과 관련된 작업을 수행합니다.

BTW 몇 년 전에 저는 멀티 스레드 응용 프로그램을 작성 했으므로 여기에 std::string에 문제가있었습니다. 내가 알았 듯이 - STL 구현 (Dunkimware)은 참조 횟수가 이 아닌 스레드 안전 인 반면 실제로는 문자열을 참조 횟수로 구현했습니다.

어쩌면 이런 일이 당신에게도 일어날 수 있습니까? 아마도 멀티 스레드 응용 프로그램을 빌드 할 때 컴파일러 플래그/옵션을 설정해야합니까?

+0

valdo, 업데이트 해 주셔서 감사합니다. 네, 똑같은 문제 같아요. 모든 것을 C 스타일 문자열로 전환하면 오류가 사라집니다. – Nik

+0

그럼에도 불구하고 유용한 게시물입니다. 이제 Valgrind로 내 응용 프로그램을 테스트한다고 생각합니다. 멀티 스레딩을 많이 사용합니다. 문자열 정보 : "C 스타일 문자열"로 전환하면 문제가 사라집니다. 하지만 "C 스타일 문자열"이라고 부르는 것은 무엇입니까? 보다 정확하게 - 그들의 조작 및 수명에 대한 귀하의 정책은 무엇입니까? 문자열이 참조 횟수에 상관없이 문제가 사실과 관련 있다고 생각합니다. 의미 - 한 문자열을 다른 문자열에 할당 할 때 실제로 문자열의 복사본을 만들거나 동일한 문자열을 할당합니까? 참조 카운팅 된 경우 - 스레드로부터 안전합니까? – valdo

+0

STL은 올바르게 잠 그거나 다중 스레드 읽기를 수행하는 경우 스레드에서 스레드를 사용하는 것이 문제가되지 않는다는 점에서 스레드 안전성이 있어야합니다. – Nikko

0

Nevermind. 나는 바보이고 C++ 문자열이 변경 가능하다는 사실을 잊어 버렸습니다. C 스타일 문자열을 사용하도록 코드를 변경했으며 경쟁 조건 문제가 사라졌습니다.

이 게시물을 읽는 사람을위한 제쳐두고, 누구든지 C++을위한 훌륭한 불변의 문자열 라이브러리를 알고 있습니까? 나는 부스트가 하나 있다고 생각했지만, 나는 그것에 관해 결정적인 것을 찾을 수 없었다.

감사합니다.

+0

모든 것이 올바르게 보호되면 아무 문제없이 스레드와 std :: strings (또는 STL containres ..)로 작업했습니다. 어떤 컴파일러/STL을 사용합니까? – Nikko

+0

gcc와 linux는 문제가 없었습니다. – Nikko

+0

C++ stl 문자열의 알려진 문제점. 그들은 참조 횟수를 사용하고 변경 가능하기 때문에이 문제는 상당히 정기적으로 발생하는 것으로 보입니다. gcc 4.1을 CentOS 5 – Nik

0

STL은 당신이 제대로 잠글 경우 스레드를 사용하는 문제가되지 않습니다 아니면 그냥 멀티 스레드를 수행하는 경우

Surpirse을 읽는 스레드 안전 의미에서 있어야하는데! 예, 이라고 가정하고라고 알려 드리지만 실제적으로 에 대해 상점에 알려 드리겠습니다..

나는 멀티 스레드 응용 프로그램을 사용했습니다. 문자열이있는 데이터 구조가 있습니다 (std::string). 그것은 중요한 섹션으로 잠겼습니다.

결국 다른 개체는 거기에서 문자열을 가져와야합니다. 같은 전략은 그 문자열을 조정하는 데 사용 된

// Take a string 
std::string str; 
{ 
    Autolock l(g_CritSect); 
    str = g_SomeStr; 
} 

:

// Put a string 
std::string str; 
{ 
    Autolock l(g_CritSect); 
    g_SomeStr = str; 
} 

그리고, 어떤 생각 그들은 다음과 같은 방법으로 그 문자열의 복사본을 만들어? 충돌!

하지만 왜? 문자열의 할당 문이 실제로 문자열을 포함하는 메모리 블록의 복사본을 생성하지 않기 때문에. 대신 동일한 메모리 블록이 재사용 (참조)됩니다.

글쎄, 이것은 반드시 나쁜 것은 아닙니다. 하지만 나쁜 점은 std :: string은 스레드로부터 안전한 방법이 아닌 문자열 참조 카운트를 구현했기 때문입니다. InterlockedIncrement 대신에 일반 산술 ++ 및 -를 사용했습니다.

str 개체는 동일한 문자열을 참조합니다. 그리고 나서 결국 소멸자에서 (또는 다른 문자열에 명시 적으로 할당 될 때) 그것을 역 참조합니다. 그리고 이것은 잠긴 지역의 밖에 밖에 있습니다.

이러한 문자열은 멀티 스레드 응용 프로그램에서 사용되지 않을 수 있습니다. 실제 참조 된 데이터가 객체에서 객체로 조용히 전달되기 때문에 정확한 로킹을 구현하는 것이 거의 불가능합니다.

STL 구현은 스레드로부터 안전하다고 선언되었습니다.

+0

아마도 복사를 실제로 복사하도록 강제 할 수 있습니다. g_SomeStr = str.c_str()? [분명히 끔찍한 해결 방법이며, bork되지 않은 STL로 전환하는 것이 더 좋습니다.] –

+0

예 항상 잘못된 구현이 있지만 항상 그런 것은 아닙니다. – Nikko

2

이 보고서는 무시 될 수 있습니다. libstdC++에서 std :: string이 구현되는 방법에 의해 트리거됩니다. 이 문제는 gcc 4.4.4 이상에 포함 된 libstdC++ 버전에서 해결되었습니다. 자세한 내용은 GCC bugzilla item #40518을 참조하십시오.

관련 문제