C++ 线程程序会导致很多竞争条件

C++ 线程程序会导致很多竞争条件,c++,multithreading,boost,thread-safety,boost-thread,C++,Multithreading,Boost,Thread Safety,Boost Thread,我有一个使用boost::threads进行多线程的程序。不幸的是,drd(valgrind--tool=drd./my_program)报告了大约10000个问题 我不确定我是否误解了boost thread的某些内容。我花了几个小时试图找出我的错误,但没有得到更多的帮助,因此任何帮助都将不胜感激 我尝试通过管道传输某些过滤器,并希望能够通过使用run调用最后一个过滤器来运行它们。然后这个过滤器应该首先调用他所依赖的所有前置过滤器,最后调用他的processQueue()方法。 现在,我希望能

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

我不确定我是否误解了boost thread的某些内容。我花了几个小时试图找出我的错误,但没有得到更多的帮助,因此任何帮助都将不胜感激

我尝试通过管道传输某些过滤器,并希望能够通过使用run调用最后一个过滤器来运行它们。然后这个过滤器应该首先调用他所依赖的所有前置过滤器,最后调用他的processQueue()方法。 现在,我希望能够在它们的WIN线程中调用前置过滤器,这样,如果图形是并行路径,我就可以得到一个加速。因此,我添加了threadgroup,这样每个前置过滤器都在自己的线程中执行。但不幸的是,我得到了很多比赛条件,我不知道他们的结果从哪里来。 我希望现在我想要实现的目标更加明确

更新

我已经将代码更新为一个更简单的代码,问题仍然存在。我认为这个问题与线程生成有关

更新2

我认为主要原因是valgrind的假阳性率很高。关于这一点,我提出了一个新问题

更新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;

}
;
#包括
#包括
#包括
#包括
#包括
类过滤器
{
公众:
过滤器(int n):
n_un(n),前驱体(0)
{
}
~Filter()
{
}
空心接头(过滤器和f)
{
前体=&f;
}
无效运行()
{
如果(!iscalculationOne_u2;){
如果(前体){
boost::thread thread(&Filter::run,preduce);
thread.join();
}
这个->处理队列(2);
IscalculationOne=真;
}
}
无效处理队列(未签名的N)
{
//做一些计算
}
公众:
int n_;
过滤器*前驱体;
布尔是计算的;
};
int main(int argc,char*argv[])
{
过滤器*f1=新过滤器(1);
过滤器*f2=新过滤器(2);
f2->连接(*f1);
f2->运行();

我不确定你的程序实际上应该做什么,但是如果你执行独立于线程的操作,比如一个数学公式,它不需要任何你想要执行的其他进程的输入,那么线程通常是有用的,因为在任何其他情况下,线程必须等待其他进程给出这些数据因此,您可能会浪费大量CPU时间。但由于这种情况不可避免,线程技术就是以尽可能短和少的方式实现您的问题

在实现线程时,还存在两个线程需要一个资源(如变量)的问题,一个线程可能在另一个线程读取资源时更改资源,因此可能会提供不一致的数据(如果一个线程比另一个线程快,那么您的程序可能会运行完全不同),这实际上被称为竞争条件,为了防止这种情况发生,需要防止同时读写,并使用某些函数让某个线程等待另一个线程

我的猜测是,这两种情况中的一种发生在您的程序中,因此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::唯一锁(filterMutex);
我想这可能是第一种情况

链接可能有助于解释您的vallgrind输出。特别是“8.2.9.Debugging OpenMP Programs”部分可能会对您产生干扰,因为实际上给出了非常类似的输出作为示例


这是一个教程,它似乎实际经历了所有这些场景(甚至更多),并很好地解释了如何使用boost线程。

您正在创建8个筛选器。每个筛选器对象都有自己的filterMutex \-它们彼此无关

您正在创建超过8个线程。这是故意的吗

每次调用
run
都会为每个前兆启动一个新线程,为前兆筛选器对象在该线程上调用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个线程(除了主线程之外),调用
f3->run
两次,调用
f1->run
三次

对同一对象上运行的多个调用将被序列化。不同的筛选器不会被序列化


我不确定这些是否导致了您的问题,但正是这类事情让我对设计以及它应该做什么产生了疑问。

您并不孤单:请看此处,这表明问题是假阳性“可能是由于新创建的线程从终止的线程重新使用了线程本地存储的内存造成的。”

你能不能先说说你想实现什么?我个人觉得很难理解。(但我不是那么聪明)为什么要使用动态分配?它不仅没有必要,而且还在泄漏。@AndersK。我已经添加了explanation@GMan我使用了Noimy Boost SydRypTR,但在最小例中我不想使用它们。@ Tune2FS:那么您应该使它真正地最小化。好的C++代码比在代码>主< <代码> >中的要小得多。由于线程是在生成之后开始的,所以connect函数不应该导致任何问题,或者我错了吗?它应该创建比需要的线程更多的线程
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