我创建了一个生产者/消费者代码,如下所示
class CTest{
public:
void producer( int i ){
unique_lock<mutex> l(m);
q.push(i);
if( q.size() )
cnd.notify_all();
}
void consumer(int i ){
unique_lock<mutex> l(m);
while( q.empty() ){
cnd.wait(l );
}
if( q.empty())
return;
cout << "IM AWAKE :" << i << endl;
int tmp = q.front();
q.pop();
l.unlock();
cout << "Producer got " << tmp << endl;
}
void ConsumerInit( int threads ){
for( int i = 0; i < threads; i++ ){
thrs.push_back(thread(&CTest::consumer, this ,i));
}
}
void waitForTHreads(){
for( auto &a : thrs )
a.join();
}
void printQueue(){
while( ! q.empty()){
int tmp = q.front();
q.pop();
cout << "Queue got " << tmp << endl;
}
}
private:
queue<int> q;
vector<thread> thrs;
mutex m;
condition_variable cnd;
};
和主要
int main(){
int x;
CTest t;
int counter = 0;
while( cin >> x ){
if( x == 0 ){
cout << "yay" << endl;;
break;
}
if( x == 1)
t.producer(counter++);
if( x == 2 )
t.ConsumerInit(5);
}
t.waitForTHreads();
t.printQueue();
return 0;
}
当用户输入&#34; 1&#34;当用户输入&#34; 2&#34;它会在队列中添加数字。 ,产生5个线程以从队列中检索数据并打印它。然而,当我输入时,我的问题是如下 6个数字,其中只有5个被打印,因为事实上只产生了5个线程,我想做的是线程从队列中检索数据,打印int,然后再次等待它是否可以打印另一个数据。这样所有N>只有5个线程打印出5个数字。
我的问题是,如何实现这一目标的标准方法是什么?我读了很少的文件,但没有fint /想不出好的解决方案。这样的问题怎么解决了?
当我尝试创建简单的线程池时:
void consumer(int i ){
while(true){
{
unique_lock<mutex> l(m);
while( q.empty() ){
cnd.wait(l );
}
if( q.empty())
return;
cout << "IM AWAKE :" << i << endl;
int tmp = q.front();
q.pop();
cout << "Producer " << i << " got " << tmp << endl;
} //consumer(i);
}
}
并输入N数字所有数字都由一个线程处理。 谢谢你的帮助!
答案 0 :(得分:0)
当前版本的consumer
只能在退出前读取一个值。为了阅读更多内容,它必须循环播放,这会导致您的第二版consumer
出现两个问题:
幸运的是,在您需要它们之前,您不会创建线程,并且在队列为空后它们会终止,因此conditional_variable
的整个处理可能会消失。
void consumer(int i)
{
unique_lock<mutex> l(m);
while (!q.empty())
{
int tmp = q.front();
q.pop();
cout << i << " got " << tmp << endl;
// note: In the real world, locking around a cout is gross. cout is slow,
// so you want the unlock up one line. But...! This allows multiple threads
// to write to the consle at the same time and that makes your output
// look like it was tossed into a blender, so we'll take the performance hit
l.unlock(); // let other threads have a turn
this_thread::yield();
l.lock(); // lock again so the queue can be safely inspected
}
}
如果你需要使用线程池方法,事情变得有点混乱,条件变量会返回。
void consumer(int i)
{
while (true)
{
unique_lock<mutex> l(m);
if (q.empty())
{
cnd.wait(l);
}
if (!q.empty()) // OK. We got out of the conditional wait, but have
// other threads sucked the queue dry? Better check.
{
int tmp = q.front();
q.pop();
cout << i << " got " << tmp << endl;
}
l.unlock();
this_thread::yield();
}
}
atomic<bool> terminated
可能有助于允许有序关机while (true)
不允许。
答案 1 :(得分:0)
通常,在不进入代码细节的情况下,创建线程池并将线程置于等待状态(等待一个或多个事件/信号,或者在您的情况下condition_variable cnd;
) - I&#39; m曾经处理过事件,所以我将在下面的文本中使用它,但condition_variable
应该以类似的方式工作。
将任务添加到队列时,会设置/触发任务事件,并唤醒一个或多个线程(取决于事件(单个/多个))。
当线程被唤醒时,它会检查(带锁)是否有任务可用,如果可用,则执行任务,如果有更多任务等待,则再次完成检查(!)。 (因为当你一次添加8个任务时,5个线程变为活动状态,因此他们需要在完成第一个任务后检查是否有更多任务。
如果没有剩余作业,则线程将返回等待状态(等待下一个作业或退出事件)。
当退出应用程序时,为所有线程设置了另一个,比如quit-event(你不能等待线程完成,因为线程本身正在等待一个事件来做一些工作) - - 或者您可以触发相同的事件,并首先设置volatile variable
,然后线程应首先检查任何事件以查看是否需要退出或执行其他工作。然后你可以等待线程回家了。
锁应尽可能短。
至于你的代码:
void producer( int i ){
unique_lock<mutex> l(m);
q.push(i);
if( q.size() )
cnd.notify_all();
}
此处锁定的持续时间超过了所需的时间(也可能太长)。你也只是推了一个值,所以q
不会是空的(不需要检查)。由于您只添加了一个项目(任务),因此只应唤醒一个线程(因此notify_one()
应该没问题。)
所以你应该:lock, push, unlock, notify
- 而不是unlock
,你可以将lock
和push
置于括号内,这将触发unlock
unique_lock<>
析构函数。
void consumer(int i ){
unique_lock<mutex> l(m);
while( q.empty() ){
cnd.wait(l );
}
if( q.empty())
return;
cout << "IM AWAKE :" << i << endl;
int tmp = q.front();
q.pop();
l.unlock();
cout << "Producer got " << tmp << endl;
}
在这里你应lock, check queue, pop if there is a task, unlock
,如果没有任务,再将线程置于等待状态,否则使用弹出值(解锁后),然后再次检查是否还有更多工作要做。通常情况下,在数据被锁定时调用cout
不是一个好主意..但是对于一个小测试你可以逃脱它,特别是因为cout
也需要同步(但它会是清理器自己同步cout
,与数据锁分开。)
void printQueue(){
while( ! q.empty()){
int tmp = q.front();
q.pop();
cout << "Queue got " << tmp << endl;
}
}
确保您的数据也在这里锁定! (虽然它只在线程完成后才从main调用,但是函数在你的类中,并且数据应该被锁定)。