boost :: threads程序会导致很多竞争条件

时间:2011-11-02 19:11:58

标签: c++ multithreading boost thread-safety boost-thread

我有一个程序,我使用boost :: threads进行多线程处理。不幸的是,drd(valgrind --tool=drd ./my_program)报告了很多关于10000的问题。

我不确定我是否误解了某些提升线程。我试着找出我的错误几个小时,但没有进一步,所以任何帮助将不胜感激。

我尝试管道某些过滤器,并希望能够通过运行调用最后一个过滤器来运行它们。然后,这个过滤器应首先调用他依赖的所有前体过滤器,最后调用他的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;

}
;

3 个答案:

答案 0 :(得分: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个Filter对象,您可以创建10个线程(除主线程外),两次调用f3->run,然后调用f1->run三次。

对同一对象的run的多次调用将被序列化。不同的过滤器不是序列化的。

不确定这是否会导致您的问题,但这种情况让我对设计感到疑惑,以及它应该做什么。

答案 1 :(得分:1)

你并不孤单:在这里看到thread,这表明问题是误报“可能是由新创建的线程从终止线程重用线程本地存储的内存引起的”。

答案 2 :(得分: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。