我是多线程编程的新手,我想实现以下功能。
这是我的设计过程,如果我错了,请纠正我。
这是一些伪代码。
boost::mutex mutex;
double x;
void ProducerThread()
{
{
boost::scoped_lock lock(mutex);
x = rand();
counter++;
}
notify(); // wake up consumer thread
}
void ConsumerThread()
{
counter = 0; // reset counter, only process the latest value
... do something which takes 5 milli-seconds ...
if (counter > 0)
{
... execute this function again, not too sure how to implement this ...
}
else
{
... what happen if producer generates a new value here??? ...
sleep();
}
}
感谢。
答案 0 :(得分:2)
如果我理解您的问题,对于您的特定应用,消费者只需要处理生产者提供的最新可用值。换句话说,价值被放弃是可以接受的,因为消费者无法跟上生产者的步伐。
如果是这种情况,那么我同意你可以在没有队列的情况下离开并使用计数器。但是,共享计数器和值变量需要 atomically 。
您可以使用boost::condition_variable
向消费者发出通知,表明新值已准备就绪。这是一个完整的例子;我会让评论做解释。
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/locks.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
boost::mutex mutex;
boost::condition_variable condvar;
typedef boost::unique_lock<boost::mutex> LockType;
// Variables that are shared between producer and consumer.
double value = 0;
int count = 0;
void producer()
{
while (true)
{
{
// value and counter must both be updated atomically
// using a mutex lock
LockType lock(mutex);
value = std::rand();
++count;
// Notify the consumer that a new value is ready.
condvar.notify_one();
}
// Simulate exaggerated 2ms delay
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
}
}
void consumer()
{
// Local copies of 'count' and 'value' variables. We want to do the
// work using local copies so that they don't get clobbered by
// the producer when it updates.
int currentCount = 0;
double currentValue = 0;
while (true)
{
{
// Acquire the mutex before accessing 'count' and 'value' variables.
LockType lock(mutex); // mutex is locked while in this scope
while (count == currentCount)
{
// Wait for producer to signal that there is a new value.
// While we are waiting, Boost releases the mutex so that
// other threads may acquire it.
condvar.wait(lock);
}
// `lock` is automatically re-acquired when we come out of
// condvar.wait(lock). So it's safe to access the 'value'
// variable at this point.
currentValue = value; // Grab a copy of the latest value
// while we hold the lock.
}
// Now that we are out of the mutex lock scope, we work with our
// local copy of `value`. The producer can keep on clobbering the
// 'value' variable all it wants, but it won't affect us here
// because we are now using `currentValue`.
std::cout << "value = " << currentValue << "\n";
// Simulate exaggerated 5ms delay
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
}
}
int main()
{
boost::thread c(&consumer);
boost::thread p(&producer);
c.join();
p.join();
}
我最近在考虑这个问题,并意识到这个解决方案虽然可行,但并不是最优的。你的制作人正在使用所有的CPU来丢弃一半的计算值。
我建议您重新考虑您的设计,并在生产者和消费者之间使用有界阻塞队列。这样的队列应该具有以下特征:
使用这种类型的队列,您可以有效地限制生产者,使其不超过消费者。它还确保生产者不会浪费CPU资源计算将被丢弃的值。
TBB和PPL等库提供了并发队列的实现。如果您想尝试使用std::queue
(或boost::circular_buffer
)和boost::condition_variable
推送自己的广告,请查看此博客的example。
答案 1 :(得分:1)
简短的回答是,你几乎肯定是错的。
对于生产者/消费者,你几乎需要两个线程之间的队列。基本上有两种选择:要么你的代码不会简单地丢失任务(这通常等于完全没有工作),否则你的生产者线程需要阻止消费者线程在它产生一个项目之前是空闲的 - 有效地转换为单线程。
目前,我将假设您从rand
返回的值应该代表要执行的任务(即,生产者生成并由消费者使用的值) 。在这种情况下,我会编写如下代码:
void producer() {
for (int i=0; i<100; i++)
queue.insert(random()); // queue.insert blocks if queue is full
queue.insert(-1.0); // Tell consumer to exit
}
void consumer() {
double value;
while ((value = queue.get()) != -1) // queue.get blocks if queue is empty
process(value);
}
这样,几乎将所有联锁降级为队列。两个线程的其余代码几乎完全忽略了线程问题。
答案 2 :(得分:0)
如果您正在进行管理,实施管道实际上非常棘手。例如,您必须使用条件变量来避免您在问题中描述的那种竞争条件,避免在实现“唤醒”消费者等机制时忙于等待...即使使用“队列”只是1元素不会使你免于一些复杂性。
通常使用专门为此目的开发和经过广泛测试的专业库会更好。如果您可以使用Visual C ++特定的解决方案,请查看Parallel Patterns Library和Pipelines的概念。