2010-06-14 22 views
7

Tôi đang sử dụng Valgrind --tool = drd để kiểm tra ứng dụng của tôi sử dụng Boost :: thread. Về cơ bản, ứng dụng sẽ điền một tập hợp các giá trị "Sách" với các giá trị "Kehai" dựa trên đầu vào thông qua kết nối ổ cắm.Không thể biết được tình trạng chủng tộc đang xảy ra ở đâu

Trên một chuỗi riêng biệt, người dùng có thể kết nối và nhận sách gửi cho họ.

Nó khá đơn giản, vì vậy tôi đã tìm cách sử dụng tăng :: mutex :: scoped_lock trên vị trí tuần tự hóa sách và vị trí xóa dữ liệu sách phải đủ để ngăn chặn mọi điều kiện chủng tộc. Đây là mã:

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; 
    } 

Hàm clear() và hàm copyChangedKehaiToString() được gọi trong chuỗi gửi dữ liệu và chuỗi gửi dữ liệu tương ứng. Ngoài ra, như một ghi chú, lớp Book:

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; 
     }; 

Khi sử dụng valgrind --tool = DRD, tôi nhận được lỗi tình trạng chủng tộc như hình dưới đây:

==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) 

Đối với cuộc sống của tôi , tôi không thể biết được tình trạng của cuộc đua ở đâu. Theo như tôi có thể nói, thanh toán bù trừ kehai chỉ được thực hiện sau khi đã thực hiện mutex, và điều tương tự cũng đúng với việc sao chép nó vào một chuỗi. Có ai có bất kỳ ý tưởng gì có thể gây ra điều này, hoặc nơi tôi nên nhìn?

Cảm ơn bạn.

Trả lời

6

Sau bài đăng của bạn, tôi đã dành thời gian để tìm hiểu về Valgrind và cách đọc đầu ra của nó.

tôi có thể thấy như sau:

Bạn có thể gọi Book::clear mà lần lượt gọi Book::Kehai::clear, nơi bạn gán giá trị cho một chuỗi. Bên trong các std::string::assign STL làm một cái gì đó mà lưu trữ một số giá trị tại địa chỉ 0x0658fbb0.

Trong khi đó, chuỗi khác đã truy cập cùng một vị trí bộ nhớ, do đó tình trạng này được coi là một điều kiện chủng tộc.

Bây giờ hãy xem "ngữ cảnh" của chủ đề khác. Valgrind không hiển thị vị trí ngăn xếp chính xác của nó, tuy nhiên nó hiển thị giữa "phân đoạn" nào mà nó đã xảy ra. Theo Valgrind một phân đoạn là một khối liên tiếp của bộ nhớ truy cập bị ràng buộc bởi các hoạt động đồng bộ hóa.

Chúng ta thấy rằng khối này bắt đầu với pthread_mutex_unlockkết thúc tại pthread_mutex_lock. Có nghĩa là - cùng một vị trí bộ nhớ đã được truy cập khi mutex của bạn không bị khóa và chuỗi đó nằm ở đâu đó bên ngoài hai chức năng của bạn.

Bây giờ, nhìn vào thông tin vị trí bộ nhớ mâu thuẫn:

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

Các BSS có nghĩa rằng đó là một biến toàn cầu/tĩnh. Và nó được định nghĩa ở đâu đó bên trong libstdc.

Kết luận:

Điều kiện chủng tộc này không liên quan gì đến cấu trúc dữ liệu của bạn. Nó liên quan đến STL. Một chuỗi thực hiện một cái gì đó để một số std::string (gán nó cho một chuỗi rỗng để được chính xác), trong khi các chủ đề khác có thể làm một cái gì đó liên quan đến STL là tốt.

BTW Tôi nhớ vài năm trước, tôi đã viết một ứng dụng đa luồng và đã xảy ra sự cố với std::string ở đó. Như tôi đã phát hiện ra - việc triển khai STL (là một Dunkimware) thực sự đã thực hiện chuỗi như là tham chiếu, trong khi số tham chiếu là không phải là an toàn chỉ.

Có thể đây cũng là những gì xảy ra với bạn? Có lẽ bạn nên đặt một số cờ/tùy chọn biên dịch khi xây dựng một ứng dụng đa luồng?

+0

valdo, cảm ơn bạn đã cập nhật. Vâng, tôi nghĩ rằng đó chính là vấn đề chính xác. khi tôi chuyển mọi thứ thành chuỗi kiểu C, các lỗi đã biến mất. – Nik

+0

Tuy nhiên, đó là một bài đăng hữu ích. Bây giờ tôi nghĩ rằng để thử nghiệm các ứng dụng của tôi với Valgrind, tôi sử dụng đa luồng rất nhiều. Giới thiệu về chuỗi: Bạn nói rằng sự cố sẽ biến mất khi bạn chuyển sang "Chuỗi kiểu C". Nhưng những gì bạn gọi là "chuỗi kiểu C"? Chính xác hơn - chính sách của bạn về thao tác và tuổi thọ của họ là gì. Tôi tin rằng vấn đề có liên quan đến thực tế nếu các chuỗi của bạn được tính tham chiếu hay không. Có nghĩa là - khi bạn gán một chuỗi cho một chuỗi khác: bạn có thực sự tạo một bản sao của chuỗi của mình hay bạn phân bổ một chuỗi khác giống với chuỗi đó không? Và nếu nó được tính tham chiếu - nó có an toàn không? – valdo

+0

STL được cho là an toàn theo luồng theo nghĩa là không phải là vấn đề khi sử dụng chúng với chuỗi nếu bạn khóa chính xác hoặc nếu bạn chỉ thực hiện đọc nhiều luồng – Nikko

0

Nevermind. Tôi là một thằng ngốc và đã quên rằng chuỗi C++ có thể thay đổi được. Tôi đã thay đổi mã để sử dụng các chuỗi kiểu c và các vấn đề về tình trạng chủng tộc của tôi đã biến mất.

Để dành cho bất kỳ ai đọc bài đăng này, có ai biết thư viện chuỗi không thay đổi tốt cho C++ không? Tôi nghĩ rằng tăng có một, nhưng tôi đã không thể tìm thấy bất cứ điều gì kết luận về nó.

Cảm ơn.

+0

Tôi đã làm việc với các chủ đề và std :: strings (hoặc STL containres ..) mà không có bất kỳ vấn đề gì nếu mọi thứ được bảo vệ chính xác. Trình biên dịch/STL nào bạn sử dụng? – Nikko

+0

Với gcc và linux tôi chưa bao giờ gặp vấn đề .. – Nikko

+0

vấn đề đã biết của nó với chuỗi st ++ C++. Kể từ khi họ sử dụng tính tham chiếu và có thể biến đổi vấn đề này dường như đi lên khá thường xuyên. sử dụng gcc 4.1 trên Centos 5 – Nik

0

STL được coi là thread-safe theo nghĩa là nó không phải là một vấn đề để sử dụng chúng với chủ đề nếu bạn khóa một cách chính xác hoặc nếu bạn chỉ cần thực hiện multi-thread đọc

Surpirse! Có, đó là được cho là, nhưng hãy để tôi cho bạn biết một cửa hàng về những gì thực sự đã xảy ra.

Tôi đã có ứng dụng đa ngôn ngữ. Có một cơ sở hạ tầng với các chuỗi (std::string). Nó đã bị khóa với một phần quan trọng.

Các đối tượng khác cuối cùng cũng cần có chuỗi từ đó. Họ tạo ra một bản sao của những chuỗi trong các cách sau đây:

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

Chiến lược tương tự đã được sử dụng để điều chỉnh những chuỗi:

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

Và đoán những gì? Sự cố!

Nhưng tại sao? Bởi vì câu lệnh gán của chuỗi không thực sự tạo ra một bản sao của khối bộ nhớ đang giữ chuỗi. Thay vào đó, cùng một khối bộ nhớ được sử dụng lại (tham chiếu).

Vâng, điều này không nhất thiết phải là xấu. Nhưng điều xấu là std :: chuỗi thực hiện tham chiếu đếm của các chuỗi không theo một cách an toàn thread. Nó sử dụng số học thông thường ++ và - thay vì InterlockedIncrement và v.v.

Điều gì đã xảy ra là đối tượng tham chiếu str cùng một chuỗi. Và sau đó nó cuối cùng dereferences nó trong destructor của nó (hoặc khi rõ ràng được giao cho một chuỗi). Và điều này xảy ra bên ngoài của khu vực bị khóa.

Để các chuỗi đó không được sử dụng trong ứng dụng đa luồng. Và hầu như không thể thực hiện khóa chính xác để làm việc này xung quanh, bởi vì dữ liệu tham chiếu thực tế trôi qua âm thầm từ đối tượng đến đối tượng.

việc triển khai STL đã được khai báo an toàn chỉ.

+0

Có lẽ bạn có thể buộc sao chép thực sự sao chép, ví dụ: g_SomeStr = str.c_str()? [Rõ ràng là một công việc khủng khiếp xung quanh, và tốt hơn để chuyển sang một STL không borked.] –

+0

Có luôn luôn có thực hiện xấu, nhưng nó không phải luôn luôn như vậy – Nikko

2

Báo cáo này có thể bị bỏ qua an toàn. Nó được kích hoạt bởi cách std :: string được thực hiện trong libstdC++. Vấn đề này đã được giải quyết trong phiên bản libstdC++ đi kèm với gcc 4.4.4 hoặc mới hơn. Xem thêm GCC bugzilla item #40518 để biết chi tiết.

Các vấn đề liên quan