互斥量既然能解决问题,为什么要用条件变量?

发布于 2022-09-11 18:22:23 字数 2366 浏览 12 评论 0

新手刚刚接触多线程,可能问题有点幼稚,但是还是想问一下:条件变量相比较于直接使用互斥量,到底优势在哪里呢?查看了一些网上的资料,有的解释是通过条件变量的wait来使线程在没有必要执行的时候阻塞住,从而降低CPU的占用率。但是我发现用条件变量和不用条件变量对于CPU的占用率是差不多的。

比如说我用多线程和deque来模拟实现对消息队列的读写:(程序有点长,烦请过目)

//未使用条件变量
class MsgList
{
public:
    void MsgWrite()  //写线程
    {
        for (int i = 0; i < 10000; i++)
        {
            lock_guard<mutex> lck(m);
            msgQue.push_back(i);
            cout << "Write Message : " << i << endl;
        }
    }
    void MsgRead()  //读线程
    {
        while (true)
        {
            if (!msgQue.empty())
            {
                lock_guard<mutex> guard(m);
                cout << "Read Message : " << msgQue.front() << endl;
                msgQue.pop_front();
            }
            else
            {
                lock_guard<mutex> guard(m);
                cout << "There is no Message !" << endl;
            }
        }
    }
private:
    deque<int> msgQue;
    mutex m;
};
int main()
{
    MsgList myMsg;
    thread ReadThread(&MsgList::MsgRead, &myMsg);
    thread WriteThread(&MsgList::MsgWrite, &myMsg);
    ReadThread.join();
    WriteThread.join();

    return 0;
}

CPU占用率:
clipboard.png

//使用条件变量(其他部分和前面一样)
class MsgList
{
public:
    void MsgWrite()
    {
        for (int i = 0; i < 10000; i++)
        {
            unique_lock<mutex> lck(m);
            msgQue.push_back(i);
            cout << "Write Message : " << i << endl;
            mycond.notify_one();   //唤醒wait
        }
    }
    void MsgRead()
    {
        while (true)
        {
            unique_lock<mutex> lck(m);
            mycond.wait(lck, [this]() {
                return !msgQue.empty();     //条件变量
            });
            int ReadData = msgQue.front();
            msgQue.pop_front();
            cout << "Read Message : " << ReadData << endl;
        }
    }
private:
    deque<int> msgQue;
    mutex m;
    condition_variable mycond;

CPU占用率:
clipboard.png

由此我发现二者对CPU的影响是差不多的,那么使用条件变量的好处到底在哪呢?是我写的这段程序体现不出二者的差异
?还是我本身对条件变量的理解有误呢?

恳请指教,谢谢!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

放赐 2022-09-18 18:22:23

上面的程序会有源源不断的资源push到队列中,当然区别就不大了。
如果是生产者的资源比较少,消费者需要进行长时间等待的时候,如果用互斥锁,消费者就需要不断轮询,占用资源;而用条件变量就可以实现等待。
另外,第一个程序里面if (!msgQue.empty())不一定是原子操作哦。

举个例子,互斥锁轮询法

#include <mutex>
#include <iostream>
#include <queue>

using namespace std;

queue<int> msgs;
mutex m;
condition_variable cond;
long long loops = 0;

void writerfunc()
{
    for (int i = 0; i < 5; i++)
    {
        this_thread::sleep_for(chrono::seconds(1));
        lock_guard<mutex> lck(m);
        cout << "Write Message: " << i << endl;
        msgs.push(i);
    }
}


void readerfunc()
{
    while (true)
    {
        loops++;
        lock_guard<mutex> lck(m);
        if (!msgs.empty())
        {
            int s = msgs.front();
            cout << "Read Message: " << s << endl;
            msgs.pop();
            if (s == 4)
                break;
        }
    }
}


int main()
{
    thread reader(readerfunc);
    thread writer(writerfunc);
    writer.join();
    reader.join();
    cout << "轮询次数:" << loops << endl;
    return 0;
}

输出:

Write Message: 0
Read Message: 0
Write Message: 1
Read Message: 1
Write Message: 2
Read Message: 2
Write Message: 3
Read Message: 3
Write Message: 4
Read Message: 4
轮询次数:9249336

说明读取线程轮询空转次数非常多

条件变量等待法

#include <mutex>
#include <iostream>
#include <queue>

using namespace std;

queue<int> msgs;
mutex m;
condition_variable cond;
long long loops = 0;

void writerfunc()
{
    for (int i = 0; i < 5; i++)
    {
        this_thread::sleep_for(chrono::seconds(1));
        unique_lock<mutex> lck(m);
        cout << "Write Message: " << i << endl;
        msgs.push(i);
        cond.notify_one();
    }
}

void readerfunc()
{
    while (true)
    {
        loops++;
        unique_lock<mutex> lck(m);
        cond.wait(lck);
        if (!msgs.empty())
        {
            int s = msgs.front();
            cout << "Read Message: " << s << endl;
            msgs.pop();
            if (s == 4)
                break;
        }
    }
}

int main()
{
    thread reader(readerfunc);
    thread writer(writerfunc);
    writer.join();
    reader.join();
    cout << "轮询次数:" << loops << endl;
    return 0;
}

输出

Write Message: 0
Read Message: 0
Write Message: 1
Read Message: 1
Write Message: 2
Read Message: 2
Write Message: 3
Read Message: 3
Write Message: 4
Read Message: 4
轮询次数:5

这样就实现等待了

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文