这是一个经典的c / p问题,其中一些线程产生数据而另一些线程读取数据。生产者和消费者都共享一个const大小的缓冲区。如果缓冲区为空,则消费者必须等待,如果已满,则生产者必须等待。我正在使用信号量来跟踪完整或空闲的队列。生产者将减少信号量,增加价值和增加填充的信号量信号量。所以我试图实现一个从生成器函数中获取一些数字的程序,然后打印出数字的平均值。通过将其视为生产者 - 消费者问题,我试图节省一些时间来执行程序。 generateNumber函数导致进程有一些延迟,所以我想创建一些生成数字的线程,并将它们放入队列中。然后运行main函数的“主线程”必须从队列中读取并找到总和然后平均。所以这就是我到目前为止所做的:
#include <cstdio>
#include <cstdlib>
#include <time.h>
#include "Thread.h"
#include <queue>
int generateNumber() {
int delayms = rand() / (float) RAND_MAX * 400.f + 200;
int result = rand() / (float) RAND_MAX * 20;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = delayms * 1000000;
nanosleep(&ts, NULL);
return result; }
struct threadarg {
Semaphore filled(0);
Semaphore empty(n);
std::queue<int> q; };
void* threadfunc(void *arg) {
threadarg *targp = (threadarg *) arg;
threadarg &targ = *targp;
while (targ.empty.value() != 0) {
int val = generateNumber();
targ.empty.dec();
q.push_back(val);
targ.filled.inc(); }
}
int main(int argc, char **argv) {
Thread consumer, producer;
// read the command line arguments
if (argc != 2) {
printf("usage: %s [nums to average]\n", argv[0]);
exit(1); }
int n = atoi(argv[1]);
// Seed random number generator
srand(time(NULL));
}
我现在有点困惑,因为我不确定如何在消费者从队列中读取时(如果q不为空),如何创建生成数字的多个生成器线程(如果q未满)。我不确定在主要内容中应该包含什么内容。 同样在“Thread.h”中,您可以创建线程,互斥锁或信号量。该线程具有.run(threadFunc,arg),。join()等方法。可以锁定或解锁互斥锁。信号量方法都已在我的代码中使用。
答案 0 :(得分:4)
您的队列未同步,因此多个制作人可以同时拨打push_back
,或者在消费者呼叫pop_front
的同时...这将会中断。
使这项工作的简单方法是使用一个线程安全的队列,它可以是你已经拥有的std::queue
的一个包装器,还有一个互斥锁。
您可以首先添加一个互斥锁,并在转发到std::queue
的每个呼叫周围锁定/解锁它 - 对于一个应该足够的消费者,对于您需要融合的多个消费者{{ 1}}和front()
进入单个同步调用。
要让使用者在队列为空时阻止,您可以向包装器添加一个条件变量。
这应该足以让你在网上找到答案 - 以下示例代码。
pop_front()
将template <typename T> class SynchronizedQueue
{
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable condvar_;
typedef std::lock_guard<std::mutex> lock;
typedef std::unique_lock<std::mutex> ulock;
public:
void push(T const &val)
{
lock l(mutex_); // prevents multiple pushes corrupting queue_
bool wake = queue_.empty(); // we may need to wake consumer
queue_.push(val);
if (wake) condvar_.notify_one();
}
T pop()
{
ulock u(mutex_);
while (queue_.empty())
condvar_.wait(u);
// now queue_ is non-empty and we still have the lock
T retval = queue_.front();
queue_.pop();
return retval;
}
};
等替换为您的&#34; Thread.h&#34;给你。
答案 1 :(得分:1)
我要做的是:
此外,请记住在每个线程中添加一个休眠,否则您将固定CPU并且不会让线程调度程序成为切换上下文并与其他线程/进程共享CPU的好地方。你不需要,但这是一个很好的做法。
答案 2 :(得分:0)
当像这样管理共享状态时,您需要一个条件变量和 一个互斥体。基本模式是一个函数:
ScopedLock l( theMutex );
while ( !conditionMet ) {
theCondition.wait( theMutex );
}
doWhatever();
theCondition.notify();
在你的情况下,我可能会使条件变量和互斥量
实现队列的类的成员。要写,
conditionMet
将是!queue.full()
,所以你最终会得到一些东西
像:
ScopedLock l( queue.myMutex );
while ( queue.full() ) {
queue.myCondition.wait();
}
queue.insert( whatever );
queue.myCondition.notify();
并阅读:
ScopedLock l( queue.myMutex );
while ( queue.empty() ) {
queue.myCondition.wait();
}
results = queue.extract();
queue.myCondition.notify();
return results;
根据线程界面,可能有两个notify
函数:通知一个(唤醒单个线程),并通知所有
(唤醒所有等待的线程);在这种情况下,你需要
通知所有(或者您需要两个条件变量,一个用于空间
写,一个用于读取的东西,每个函数等待一个,
但通知对方)。
答案 3 :(得分:0)
使用互斥锁保护队列访问,应该是它。一个'计算机科学101'有限的生产者 - 消费者队列需要两个信号量,(管理空闲/空数和生产者/消费者等待,正如你已经在做的那样),以及一个互斥/ futex / criticalSection来保护队列
我不知道如何用condvars替换信号量和互斥量有什么帮助。重点是什么?如何使用condvars实现有界的生产者 - 消费者队列,该condvars适用于具有多个生产者/消费者的所有平台?
答案 4 :(得分:-1)
#include<iostream>
#include<deque>
#include<mutex>
#include<chrono>
#include<condition_variable>
#include<thread>
using namespace std;
mutex mu,c_out;
condition_variable cv;
class Buffer
{
public:
Buffer() {}
void add(int ele)
{
unique_lock<mutex> ulock(mu);
cv.wait(ulock,[this](){return q.size()<_size;});
q.push_back(ele);
mu.unlock();
cv.notify_all();
return;
}
int remove()
{
unique_lock<mutex> ulock(mu);
cv.wait(ulock,[this](){return q.size()>0;});
int v=q.back();
q.pop_back();
mu.unlock();
cv.notify_all();
return v;
}
int calculateAvarage()
{
int total=0;
unique_lock<mutex> ulock(mu);
cv.wait(ulock,[this](){return q.size()>0;});
deque<int>::iterator it = q.begin();
while (it != q.end())
{
total += *it;
std::cout << ' ' << *it++;
}
return total/q.size();
}
private:
deque<int> q;
const unsigned int _size=10;
};
class Producer
{
public:
Producer(Buffer *_bf=NULL)
{
this->bf=_bf;
}
void Produce()
{
while(true)
{
int num=rand()%10;
bf->add(num);
c_out.lock();
cout<<"Produced:"<<num<<"avarage:"<<bf->calculateAvarage()<<endl;
this_thread::sleep_for(chrono::microseconds(5000));
c_out.unlock();
}
}
private:
Buffer *bf;
};
class Consumer
{
public:
Consumer(Buffer *_bf=NULL)
{
this->bf=_bf;
}
void Consume()
{
while (true)
{
int num=bf->remove();
c_out.lock();
cout<<"Consumed:"<<num<<"avarage:"<<bf->calculateAvarage()<<endl;
this_thread::sleep_for(chrono::milliseconds(5000));
c_out.unlock();
}
}
private:
Buffer *bf;
};
int main()
{
Buffer b;
Consumer c(&b);
Producer p(&b);
thread th1(&Producer::Produce,&p);
thread th2(&Consumer::Consume,&c);
th1.join();
th2.join();
return 0;
}
Buffer 类具有双倍队列,最大 Buffer 大小为 10。 它有两个功能来添加到队列和从队列中删除。 Buffer 类具有 calculateAvarage() 函数,该函数将计算添加或删除元素的平均时间。
还有两个类,一个是具有 buffwr 类指针的生产者和消费者。 我们在消费者类中有 Consume() ,在 Producer 类中有 Produce() 。 Consume()>>锁定缓冲区并检查缓冲区的大小是否为0,它将从缓冲区中删除并通知生产者并解锁。 Produce()>>锁定缓冲区并检查缓冲区的大小是否不是最大缓冲区大小,它将添加并通知消费者并解锁。