C++条件变量

1.引入条件变量的原因:

        首先我们想象一个场景,果农每秒摘一个苹果,无数的消费者排队等苹果,苹果摘好就拿走。为此场景写一个极简生产者消费者模型如下:

#include 
#include 
#include 
#include 

std::mutex mtx;

void func1(int& apple)
{
    while (1)
    {
        {
            std::unique_lock lck(mtx);
            apple += 1; 
        }
        usleep(1000 * 1000);
    }
}

void func2(int& apple)
{
    while (1)
    {
        std::unique_lock lck(mtx);
        if (apple>0) {
            apple -= 1;
        }
    }
}

int main()
{
    int apple = 0;
    std::thread t1(func1,std::ref(apple));
    std::thread t2(func2, std::ref(apple));
    t2.join();
    t1.join();
    return 0;
}

        这个程序逻辑比较简单,func1是生产者,func2是消费者。然而这种写法非常占用cpu。在运行前我的centos cpu是这样:

C++条件变量_第1张图片

         运行后是这样:

C++条件变量_第2张图片

         从第三行%Cpu(s)可以看出用户空间占cpu百分比由1.8左右变为6.4左右,从倒数第2行看多出的时间基本都在我们的main.out上。这只是一个简单的函数,而且仅有一个消费者线程。如果消费者线程多起来那消耗将非常大。

        消耗的原因在于生产者1秒的沉睡时间里,消费函数使用while循环不断进行访问,这导致了无必要的消耗。如果给消费者设定沉睡时间,即每次访问间隔0.5s或1s虽然可以一定程度上缓解这个问题,但是还面临两个情况:

        a.生产者生产好了但是消费者还在沉睡,导致资源(苹果)没有及时被使用。

        b.消费者沉睡结束生产者沉睡未结束,导致仍然消耗cpu。

        很多时候真实的生产者生产时间是不确定的,比如这次摘苹果用0.5s,下一次用1.5s,因此无法从时间上精细控制沉睡时间,因此需要有一种方式,能够在生产者没生产好时不占cpu,一直阻塞在那里直到生产好,因此我们需要引入条件变量。

2.条件变量使用:

        条件变量需要引入头文件#include

        条件变量可以和std::mutex结合一起使用,其中notify_one()和wait(),wait()可以让线程陷入休眠状态,notify_one()可以唤醒一个处于wait中的条件变量,notify_all()可以唤醒所有出于wait的条件变量。

        使用条件变量的程序如下:

#include 
#include 
#include 
#include 
#include 
#include 

std::mutex mtx;
std::condition_variable cond;

void func1(int& apple)
{
    while (1)
    {
        {
            std::unique_lock lck(mtx);
            apple += 1; 
            cond.notify_one();//唤醒一个等待的线程
        }
        usleep(1000 * 1000);
    }
}

void func2(int& apple)
{
    while (1)
    {
        std::unique_lock lck(mtx);
        if (apple) {
            std::cout << apple << '\n';
            apple -= 1;
            continue;
        }

        cond.wait(lck);//等待线程
    }
}

int main()
{
    int apple = 0;
    std::thread t1(func1,std::ref(apple));
    std::thread t2(func2, std::ref(apple));
    t2.join();
    t1.join();
    return 0;
}

        运行后再次查看top,可以发现%Cpu(s)变为1.2,远小于未引用条件变量的占用。

C++条件变量_第3张图片

        从func2里我们发现,在wait之前互斥锁已经锁住了互斥量,然而生产者可以一直生产,这是因为wait函数调用了互斥锁的unlock函数然后才阻塞。这也是条件变量使用的加锁方式必须是unique_lock,不能是lock_guard的原因。而lock_guard没有lock和unlock接口,而unique_lock提供了,所以条件变量必须使用unique_lock保护互斥量。
 

你可能感兴趣的:(C++并发编程,c++)