如何在生产者 - 消费者场景中使用Boost条件变量?

时间:2013-02-04 08:35:58

标签: c++ multithreading boost-thread producer-consumer condition-variable

编辑:下面

我有一个线程负责从缓冲区中的设备流式传输数据。另外,我有N个线程对该数据进行一些处理。在我的设置中,我希望流式传输线程从设备获取数据,并在获取新数据或达到超时之前等待N个线程完成处理。 N个线程应该等到获取新数据后再继续处理。我相信如果我不希望N个线程在缓冲区上重复处理并且我希望处理所有缓冲区而不跳过任何缓冲区,那么这个框架应该可以工作。

仔细阅读后,我发现条件变量就是我所需要的。我已经学习了教程和其他堆栈溢出问题,这就是我所拥有的:

全局变量:

boost::condition_variable cond;
boost::mutex mut;

成员变量:

std::vector<double> buffer
std::vector<bool> data_ready       // Size equal to number of threads

数据接收器循环(1个线程运行此):

while (!gotExitSignal())
{
    {
        boost::unique_lock<boost::mutex> ll(mut);
        while(any(data_ready))
            cond.wait(ll);
    }

    receive_data(buffer);

    {
        boost::lock_guard<boost::mutex> ll(mut);
        set_true(data_ready);
    }

    cond.notify_all();
}

数据处理循环(N个线程运行此)

while (!gotExitSignal())
{
    {
        boost::unique_lock<boost::mutex> ll(mut);
        while(!data_ready[thread_id])
            cond.wait(ll);
    }

    process_data(buffer);

    {
        boost::lock_guard<boost::mutex> ll(mut);
        data_ready[thread_id] = false;
    }
    cond.notify_all();
}

这两个循环在它们自己的同一个类的成员函数中。变量缓冲区是一个成员变量,因此可以跨线程共享。

接收器线程将首先启动。 data_ready变量是大小为N的bool向量。如果数据已准备好处理,则data_ready [i]为true,如果线程已经处理了数据,则为false。如果data_ready的任何元素为true,则函数any(data_ready)输出true,否则返回false。 set_true(data_ready)函数将data_ready的所有元素设置为true。接收方线程将检查是否有任何处理线程正在处理。如果没有,它将获取数据,设置data_ready标志,通知线程,并继续循环,这将在开始时停止,直到处理完成。处理线程将检查它们各自的data_ready标志是否为真。一旦它成立,处理线程将进行一些计算,将其各自的data_ready标志设置为0,并继续循环。

如果我只有一个处理线程,程序运行正常。一旦我添加更多线程,我就会遇到处理输出是垃圾的问题。另外,处理线程的顺序由于某种原因而重要;换句话说,我启动的LAST线程将输出正确的数据,而前一个线程将输出垃圾,无论输入参数是什么用于处理(假设有效参数)。我不知道问题是由于我的线程代码还是我的设备或数据处理设置有问题。我尝试在处理和接收步骤中使用couts,并且使用N个处理线程,我看到输出应该是:

receive data
process 1
process 2
...
process N
receive data
process 1
process 2
...

条件变量的使用是否正确?可能是什么问题?

编辑:我按照fork的建议并将代码更改为:

数据接收器循环(1个线程运行此):

while (!gotExitSignal())
{
    if(!any(data_ready))
    {
        receive_data(buffer);
        boost::lock_guard<boost::mutex> ll(mut);
        set_true(data_ready);
        cond.notify_all();
    }       
}

数据处理循环(N个线程运行此)

while (!gotExitSignal())
{
    // boost::unique_lock<boost::mutex> ll(mut);
    boost::mutex::scoped_lock ll(mut);
    cond.wait(ll);

    process_data(buffer);

    data_ready[thread_id] = false;
}

它的效果更好一些。我使用正确的锁吗?

1 个答案:

答案 0 :(得分:0)

我没有阅读你的整个故事,但如果我快速查看代码,我会发现你使用了错误的条件。 条件就像一个状态,一旦你在一个等待条件下设置一个线程,它就会放弃cpu。所以你的线程将有效地停止运行,直到其他一些进程/线程通知它。

在您的代码中,您有一个while循环,每次检查等待的数据。这是错误的,它应该是一个if而不是一段时间。但话说再说它不应该存在。检查数据应该在其他地方完成。并且你的工作线程在完成工作后应该处于等待状态。

您的工作线程是消费者。生产者是提供数据的人。 我认为更好的结构是做一个线程检查是否有数据并通知工人。

PSEUDO CODE:

//producer
while (true) {

    1. lock mutex
    2. is data available
    3. unlock mutex

    if (dataAvailableVariable) {
        4. notify a worker
        5. set waiting condition
    }
}


//consumer
while (true) {
    1. lock mutex
    2. do some work
    3. unlock mutex
    4. notify producer that work is done
    5. set wait condition
}

你还应该注意这样一个事实,即为了避免死锁,某些线程需要处于活动状态,这意味着所有线程都处于等待状态。

我希望能帮到你一点。