2011-11-02 42 views
3

我有一個程序,我使用boost :: threads進行多線程。不幸的是,drd(valgrind --tool=drd ./my_program)報告了大量10000問題。boost :: threads程序會導致很多競爭條件

我不確定是否誤解了boost線程的某些內容。我試圖找出我的錯誤幾個小時,但沒有得到更多,因此任何幫助將不勝感激。

我試圖管道某些過濾器,並希望能夠通過運行調用最後一個過濾器來運行它們。這個過濾器應該先調用他所依賴的所有前體過濾器,最後再調用他的processQueue()方法。 我現在想要能夠在他們的獲勝線程中調用前體過濾器,以便我可以加速如果圖形爲並行路徑。因此我添加了線程組,以便每個前體過濾器都在他自己的線程中執行。但不幸的是,我得到了很多競爭條件,我不確定他們的結果。 我希望現在更清楚我想達到什麼。

更新

我的代碼更新到哪裏的問題仍然出現,甚至簡單的代碼。我認爲這個問題與線程生成有關。

更新2

我想這些的主要原因是Valgrind的非常高的假陽性率。我已經開了一個關於這個問題的新問題。 See here

更新3

當我使用的Valgrind的3.6.1,而不是3.7.0或3.8.0大部分錯誤是可以避免的。 DRD的

這裏一個報告:

==29905== Conflicting load by thread 1 at 0xb0081000 size 8 
==29905== at 0x25A6C2: pthread_join (in /usr/lib/system/libsystem_c.dylib) 
==29905== by 0x2BEC0: boost::thread::join() (in /usr/local/lib/libboost_thread.dylib) 
==29905== by 0x100006641: Filter::run() (in ./playgroudThreads) 
==29905== by 0x100001013: main (in ./playgroudThreads) 
==29905== Allocation context: unknown. 
==29905== Other segment start (thread 2) 
==29905== at 0x2A7B68: thread_start (in /usr/lib/system/libsystem_c.dylib) 
==29905== Other segment end (thread 2) 
==29905== at 0x3E667A: mach_msg_trap (in /usr/lib/system/libsystem_kernel.dylib) 
==29905== by 0x3DED38: semaphore_create (in /usr/lib/system/libsystem_kernel.dylib) 
==29905== by 0x2A50F7: new_sem_from_pool (in /usr/lib/system/libsystem_c.dylib) 
==29905== by 0x2A6199: _pthread_exit (in /usr/lib/system/libsystem_c.dylib) 
==29905== by 0x2A48C9: _pthread_start (in /usr/lib/system/libsystem_c.dylib) 
==29905== by 0x2A7B74: thread_start (in /usr/lib/system/libsystem_c.dylib) 

在這裏,我的例子代碼:

#include <iostream> 
#include <vector> 
#include <sys/time.h> 
#include <boost/thread.hpp> 
#include <boost/bind.hpp> 

class Filter 
{ 
    public: 

     Filter(int n) : 
       n_(n), precursor_(0) 
     { 
     } 

     ~Filter() 
     { 
     } 

     void connect(Filter& f) 
     { 
      precursor_ = &f; 
     } 

     void run() 
     { 

      if (!isCalculationDone_) { 
       if (precursor_) { 
        boost::thread thread(&Filter::run, precursor_); 

        thread.join(); 
       } 
       this->processQueue(2); 
       isCalculationDone_ = true; 

      } 

     } 

     void processQueue(unsigned N) 
     { 
      //do some calculations 

     } 

    public: 
     int n_; 
     Filter* precursor_; 

     bool isCalculationDone_; 

}; 

int main(int argc, char* argv[]) 
{ 

    Filter* f1 = new Filter(1); 
    Filter* f2 = new Filter(2); 

    f2->connect(*f1); 

    f2->run(); 

    std::cerr << "main: done" << std::endl; 
    delete f2; 
    delete f1; 
    return 0; 

} 
; 
+1

請問你可能會先說你想達到什麼目的?我個人覺得很難理解。 (但後來我不那麼聰明) –

+0

你爲什麼使用動態分配?它不僅是不必要的,而且是在泄漏它。 – GManNickG

+0

@AndersK。我已經添加了解釋 – tune2fs

回答

1

你並不孤單:在這裏看到thread,這表明這個問題是一個錯誤的肯定「可能是由於新創建的線程對已終止線程的線程本地存儲重用內存引起的」。

0

好吧,我不知道你的程序實際上是應該做的,但一般線程只是有用如果你像一個數學公式一樣獨立運算,而不需要任何其他進程的任何輸入,因爲在任何其他情況下,線程必須等待,直到另一個進程可以提供這些數據,因此你冒着浪費很多的風險的CPU時間。但是,由於這種情況是不可避免的,因此線程技術就是以這種情況儘可能短而且儘可能少的方式來實現您的問題。

在實現線程時,也存在兩個線程需要一個資源(如變量)的問題,有人可能會在另一個線程正在讀取它時更改它,因此可能會提供不一致的數據(也是您的程序如果一個線程比另一個線程更快,則可能運行完全不同),這實際上稱爲競態條件,並且爲了防止這種情況,有mutexes來防止同時讀寫,並且某些函數讓某個函數線程等待另一個。

我的猜測是,這兩種情況之一發生在您的程序中,因此vallgrind會告訴您這些問題,因此在您的位置我會通過您的整個代碼,並重新考慮任何新線程之間存在或可能存在的依賴關係。並考慮到主要部分:

f2->connect(f1); 
f3->connect(f1); 
f4->connect(f2); 
f5->connect(f3); 
f6->connect(f4); 
f6->connect(f5); 
f7->connect(f3); 
f8->connect(f6); 
f8->connect(f7); 

boost::unique_lock<boost::shared_mutex> lock(filterMutex_); 

我想這可能是第一個場景。

This鏈接可能有助於解釋你的vallgrind輸出。特別是「8.2.9。調試OpenMP程序」部分可能會因爲實際上非常類似的輸出作爲示例而受到干擾。

Here一個教程,似乎實際上經歷了所有這些場景(甚至更多),並很好地解釋瞭如何使用boost-threading。

+0

由於線程在生成後開始,連接函數不應該引起任何問題,或者我錯了嗎? – tune2fs

2

您正在創建8個過濾器。每個Filter對象都有自己的filterMutex_ - 這些對象沒有任何關係。

您正在創建8個以上的線程。這是故意的嗎?

每次調用run都會爲每個前體啓動一個新線程,在該線程上爲該前體Filter對象調用Filter :: run。所以:

f8->run creates 2 threads for its precursors, calling f6->run and f7->run 
f6->run creates 2 threads: f4->run and f5->run 
    f4->run creates 1 thread: f2->run 
    f2->run creates 1 thread: f1->run 
    f1->run creates no additional threads 
    f5->run creates 1 thread: f3->run 
    f3->run creates 1 thread: f1->run (different thread from the other f1->run) 
    f1->run creates no additional threads 
f7->run creates 1 thread: f3->run 
    f3->run creates 1 thread: f1->run 
    f1->run creates no additional threads 

那麼,你8個過濾對象,創建10個線程(除了你的主線程),調用兩次,f1->run三次。

在同一個對象上的多個調用run將被序列化。不同的過濾器不會被序列化。

不知道這是否會導致您的問題,但這是那種讓我懷疑設計,以及它應該做什麼的事情。

+0

它應該創建比需要更多的線程。如果調用processQueue函數,則會檢查結果是否已經存在,如果是,函數立即返回。 – tune2fs