2011-11-02 38 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