使用Boost C ++进行多线程处理 - 同步问题

时间:2013-05-15 18:38:49

标签: c++ multithreading boost synchronisation

我想进行多线程,其中Thread ONE将数据传递给4-5工作线程,这些线程处理数据,所有工作线程完成后我想继续。我正在使用boost来实现,但是我有同步问题。在某一点意味着程序停止并且不继续工作。

之前我使用过OpenMP并且运行良好,但我想单独设置线程优先级,我无法弄清楚如何使用OpenMP执行此操作,因此我使用了自己的解决方案:

如果有人可以提供一些提示来查找此代码中的错误,或者可以帮助我找到解决该问题的另一种方法,我将非常高兴。

谢谢你, KmgL

#include <QCoreApplication>

#include <boost/thread.hpp>

#define N_CORE 6
#define N_POINTS 10
#define N_RUNS 100000

class Sema{

public:
    Sema(int _n =0): m_count(_n),m_mut(),m_cond(){}

    void set(int _n)
    {
        boost::unique_lock<boost::mutex> w_lock(m_mut);
        m_count = -_n;
    }

    void wait()
    {
        boost::unique_lock<boost::mutex> lock(m_mut);
        while (m_count < 0)
        {
            m_cond.wait(lock);
        }
        --m_count;
    }
    void post()
    {
        boost::unique_lock<boost::mutex> lock(m_mut);
        ++m_count;
        m_cond.notify_all();
    }


private:
    boost::condition_variable m_cond;
    boost::mutex m_mut;
    int m_count;

};

class Pool
{
private:
    boost::thread m_WorkerThread;
    boost::condition_variable m_startWork;
    bool m_WorkerRun;
    bool m_InnerRun;
    Sema * m_sem;

    std::vector<int> *m_Ep;
    std::vector<int>  m_ret;

    void calc()
    {
        unsigned int    no_pt(m_Ep->size());                
        std::vector<int> c_ret;
        for(unsigned int i=0;i<no_pt;i++)
            c_ret.push_back(100 + m_Ep->at(i));

        m_ret = c_ret;
    }
    void run()
    {
        boost::mutex WaitWorker_MUTEX;
        while(m_WorkerRun)
        {
            boost::unique_lock<boost::mutex> u_lock(WaitWorker_MUTEX);
            m_startWork.wait(u_lock);
            calc();
            m_sem->post();
        }

    }

public:
    Pool():m_WorkerRun(false),m_InnerRun(false){}
    ~Pool(){}
    void start(Sema * _sem){
        m_WorkerRun = true;
        m_sem = _sem;
        m_ret.clear();
        m_WorkerThread = boost::thread(&Pool::run, this);}
    void stop(){m_WorkerRun = false;}
    void join(){m_WorkerThread.join();}

    void newWork(std::vector<int> &Ep)
    {
        m_Ep = &Ep;
        m_startWork.notify_all();
    }
    std::vector<int> getWork(){return m_ret;}



};

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

    Pool TP[N_CORE];

    Sema _sem(0);
    for(int k=0;k<N_CORE;k++)
        TP[k].start(&_sem);


    boost::this_thread::sleep(boost::posix_time::milliseconds(10));

    std::vector<int> V[N_CORE];

    for(int k=0;k<N_CORE;k++)
        for(int i=0;i<N_POINTS;i++)
        {
            V[k].push_back((k+1)*1000+i);
        }

    for(int j=0;j<N_RUNS;j++)
    {
        _sem.set(N_CORE);
        for(int k=0;k<N_CORE;k++)
        {
            TP[k].newWork(V[k]);
        }

        _sem.wait();

        for(int k=0;k<N_CORE;k++)
        {
            V[k].clear();
            V[k]=TP[k].getWork();
            if(V[k].size()!=N_POINTS)
                std::cout<<"ERROR: "<<"V["<<k<<"].size(): "<<V[k].size()<<std::endl;

        }
       if((j+1)%100==0)
            std::cout<<"LOOP: "<<j+1<<std::endl;
    }
    std::cout<<"FINISHED: "<<std::endl;

    return a.exec();
}

2 个答案:

答案 0 :(得分:0)

可以使用Boost futures完成。启动线程然后wait for all完成。无需其他同步。

答案 1 :(得分:0)

您在拨打Pool::newWork()Pool::run()之间的比赛。

您必须记住,发送/广播条件变量不是粘性事件。如果您的线程在信号发送时没有等待条件变量,则信号将丢失。这是你的程序中可能发生的事情:没有任何东西阻止你的主线程在他们有时间在你的条件变量上调用Pool::newWork()之前调用每个Pool对象上的wait()

要解决此问题,您需要将boost::mutex WaitWorker_MUTEX作为类成员移动,而不是将其作为局部变量。 Pool::newWork()需要在进行更新之前获取该互斥锁:

boost::unique_lock<boost::mutex> u_lock(WaitWorker_MUTEX);
m_Ep = &Ep;
m_startWork.notify(); // no need to use notify_all()

由于您在Pool::run()中使用了条件变量,因此需要处理虚假唤醒。我建议在构造对象时以及每次完成工作项时将m_Ep设置为NULL:

boost::unique_lock<boost::mutex> u_lock(WaitWorker_MUTEX);
while (1) {
    while (m_Ep == NULL && m_workerRun) {
        m_startWork.wait(u_lock);
    }
    if (!m_workerRun) {
        return;
    }
    calc();
    m_sem->post();
    m_Ep = NULL;
}

stop()需要获取互斥锁并通知():

boost::unique_lock<boost::mutex> u_lock(WaitWorker_MUTEX);
m_workRun = false;
m_startWork.notify();

这些改变应该让你没有必要睡10毫秒。您似乎没有致电Pool::stop()Pool::join()。您应该更改代码来调用它们。

通过在m_ret中处理Pool::calc()而不是在最后复制结果,您也可以获得更好的性能。当你返回工作时,你也在复制。您可能希望Pool::getWork()将const引用返回m_ret

我没有运行此代码,因此可能存在其他问题。它应该有助于你移动

从您的代码中可能看起来您可能想知道为什么条件变量需要与互斥锁一起使用(因为您在Pool::run()中声明了一个本地互斥锁)。我希望我的修复能让它更清晰。