消费者生产者多线程冻结 [英] Consumer producer multithreading freezes

查看:48
本文介绍了消费者生产者多线程冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个简单的消费者/生产者代码以供学习,其中生产者将数字压入堆栈,消费者线程打印数字,这是我得到的:

const int N_THREADS = 10;const int N_TESTS = 100;布尔完成=假;queueq;无效生产(){for (int i = 0; i < N_TESTS; i++){q.push(i);cv.notify_all();}完成 = 真;}无效消费(){while (!q.empty() || !finished){unique_lock锁(米);cv.wait(lock, [] {return !q.empty(); });int i = q.front();cout<<我<<结束;q.pop();}}int main(){//将用于生产的线程线程生产者(生产);//消费者线程向量向量<线程>消费者(N_THREADS);for (int i = 0; i 

但是,当我运行代码时,它会打印数字但它只是冻结,它永远不会结束:

可以做些什么来结束它?

解决方案

我发现您的代码存在一些错误.

首先为什么你调用 notify_all() 你只推送了一个元素,我认为调用 notify_one() 更好.

想象一下这个场景:生产者推送一个元素然后调用 notify_all() 然后其他线程唤醒其中一个将使用互斥锁并执行工作并弹出元素然后其他线程检查条件并查看队列为空然后他们将进入睡眠(资源浪费).

另一个重要的错误是当生产者完成他的工作时它会返回此时可能队列仍然有元素在这种情况下某些线程将等待通知但由于生产者退出因此生产者不会再发送通知,这会导致一些线程在 cv.wai() 处阻塞.即使是通知条件变量它也会阻塞,因为 !q.empty() 的条件返回 false(因为 q.empty() 在生产者完成后已经为真).

为了修复此代码,您应该修改代码的某些部分.

首先,我建议您在推入队列时也获取互斥锁,因为您可能会面临竞争条件(在这里您使用的是队列,我认为它不会在这里发生,但是如果您使用向量,您肯定会看到).

因为 cv.wait() 处的代码块(但要注意某些线程将退出,因为它们会在生产者完成之前通知,而在生产者完成后他们将检查 while 条件,他们看到完成的变量是true 因此他们有机会退出但其他线程将在 cv.wait() 处阻塞).为了解决这个问题,你应该在你的条件变量中加入另一个条件:

cv.wait(lock, [] {return !q.empty() || (finished && q.empty()); });if (完成&& q.empty())休息;

并且您的代码成功退出.

我还建议您阅读以下主题:

C++11 可以我确保 condition_variable.wait() 不会错过通知?

https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables

I'm trying to create a simple consumer/producer code for learning, in which a producer pushes numbers into a stack, and consumer threads print the numbers, heres what I got:

const int N_THREADS = 10;
const int N_TESTS = 100;
bool finished = false;
queue<int> q;

void produce()
{
    for (int i = 0; i < N_TESTS; i++)
    {
        q.push(i);
        cv.notify_all();        
    }
    finished = true;
}

void consume()
{
    while (!q.empty() || !finished)
    {   
        unique_lock<mutex> lock(m);
        cv.wait(lock, [] {return !q.empty(); });
        int i = q.front();
        cout << i << endl;
        q.pop();
    }   
}

int main()
{
    //thread that will be used for producing
    thread producer(produce);
    
    //vector of consumer threadss
    vector<thread> consumers(N_THREADS);
    for (int i = 0; i < N_THREADS; i++)
    {
        consumers[i] = thread(consume);
    }

    //joining all threads
    producer.join();
    for (int i = 0; i < N_THREADS; i++)
    {
        consumers[i].join();
    }
    
    return 0;   
}

However, when i run the code it prints the numbers but it just freezes, it never ends:

What can be done for it to end?

解决方案

i spotted some bugs on your code.

first why you call notify_all() you pushed just one element i think it's better idea to call notify_one().

imagine this senario: producer push one element then call notify_all() then others threads wakeup one of them will take mutex and do work and pop element and then other thread check condition and see queue is empty and then they will go to sleep(waste of resource).

another important bug is that when producer finished his work it will return at this moment may queue still has element in this case some thread will wait for notification but since producer exited and therefore producer will not send notification anymore and it will cause some threads block at cv.wai(). even it's notify condition variable it will block because condition of !q.empty() return false(because q.empty() is already true after producer finished).

in order to fix this code you should modify some part of your code.

first i recommend you to also acquire mutex when you are pushing to queue because it is possible that you face race condition(in here you are using queue i dont think it will happened here but if you use vector you will definitely see).

because code block at cv.wait()(but beware some thread will exit because they will notified before producer finished and after producer finished they are going to check while condition and they see finished variable is true therefore they have chance to exit but other thread will block at cv.wait()). in order to fix this problem you should put another condition in your condition variable:

cv.wait(lock, [] {return !q.empty() || (finished && q.empty()); });
if (finished && q.empty())
        break;

and your code exit successfully.

i also recommend you to read this topics:

C++11 Can I ensure a condition_variable.wait() won't miss a notification?

https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables

这篇关于消费者生产者多线程冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆