一个并发编程的系列故事

条件变量wait_for

wait_for 的重载版本(predicte(2))的最后一个参数 pred 表示 wait_for 的预测条件,只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞
阻塞的情况是: pred为false
解除阻塞的情况是
1. 达到超时时间
2. 收到通知,且pred为true

 template <class Rep, class Period, class Predicate>
 bool wait_for (unique_lock<mutex>& lck,
      const chrono::duration<Rep,Period>& rel_time, Predicate pred);

热身一下

#if 1 //条件变量

std::mutex g_mx_;
std::condition_variable g_cv_;
vector<int> g_cache_;
vector<int> g_db_;

void InsertThread()
{
    g_cache_.clear();
    while (true)
    {
        //如果缓存超过10条或时间超过3秒则入库
        time_t t1 = time(0);

        std::unique_lock<std::mutex> lock(g_mx_);
        while (g_cache_.empty())
        {
            time_t t1 = time(0);
            /*
            template <class Rep, class Period, class Predicate>
       bool wait_for (unique_lock<mutex>& lck,
                      const chrono::duration<Rep,Period>& rel_time, Predicate pred);

            wait_for 的重载版本(predicte(2))的最后一个参数 pred 表示 wait_for 的预测条件,只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞

            阻塞的情况是: pred为false
            解除阻塞的情况是: 
            1. 达到超时时间
            2. 收到通知,且pred为true
            */
            g_cv_.wait_for(lock, std::chrono::seconds(5), [](){
                return g_cache_.size()>=10;
            });
            time_t t2 = time(0);
            cout << "cost time = " << t2 - t1 << endl;
        }

        time_t t2 = time(0);
        cout << "cost time = " << t2 - t1 << endl;
        g_db_.insert(g_db_.end(), g_cache_.begin(),g_cache_.end());
        cout << "insert into db"<<endl;
        g_cache_.clear();
    }
    
}

void insert(int n){
    for(int i=0;i<n;++i){
        g_cache_.push_back(i);
    }
}
int main()
{
    thread t1(InsertThread);
    t1.detach();

    char c;
    while (cin >> c)
    {
        if(c == 'b'){
            insert(20);
            cout <<"push batch" <<endl;
            g_cv_.notify_one();
        }else{
            g_cache_.push_back(8);
            g_cv_.notify_one();
            cout <<"push one" <<endl;
        }
       
    }
    
    return 0;
    
}

#endif

故事开始了

一辆公交车,发车了

假如你要实现一个公交车系统,每十分钟发一趟车,可以设置一个定时器,每十分钟触发一次(前提是有现成的定时器工具,否则还需要另外实现)。
可是有的公交线路客流量特别大,往往不到10分钟,车就坐满了。为了优化这种情况,你可能会补充一条发车规则,如果10分钟内,公交车坐满了
就不必再等,可以直接发车。如何实现这样的需求呢? 条件变量是个不错的选择

#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <vector>
#include <future>
#include <queue>
#include <functional>
using namespace std;

#if 0

const int kMaxseats = 5;
class Bus
{
public:
    Bus() = default;
    Bus(string name) : name_(name) {}
    ~Bus() = default;
    void operator=(const Bus &) = delete;

    void departure(bool b_full = false) const
    {
        if(b_full){
            cout << "this is " << name_ << "bus, the bus is full, go..." << endl;
        }else{
            cout << "this is " << name_ << " bus, it is time to go, passenger : "<< seats_.size() << endl;
        }
    }

    void get_on() const
    {
        cout << "\t\t\t\t\t\t\tget on a passenger" <<endl;
        std::lock_guard<std::mutex> lock(mx_);
        seats_.push(1);
        cv_.notify_one();
    }
    void get_off() const
    {
        cout << "\t\t\t\t\t\t\tget off a passenger" <<endl;
        std::lock_guard<std::mutex> lock(mx_);
        seats_.pop();
    }

public:
    bool offline_ = false;
    mutable std::mutex mx_;
    mutable std::condition_variable cv_;
    string name_;
    mutable queue<int> seats_;
};

void departure_thd(Bus &bus)
{
    while (!bus.offline_)
    {
        cout << "waitting for departure ... " << endl;
        unique_lock<std::mutex> lock(bus.mx_);
        bus.cv_.wait_for(lock, std::chrono::seconds(5), [&]() {
            return bus.seats_.size() >= kMaxseats;
        });

        bus.departure(bus.seats_.size() >= kMaxseats);

        std::this_thread::sleep_for(std::chrono::milliseconds(10000));

        //simulate getting off at the station
        queue<int> tmp;
        std::swap(tmp, bus.seats_);
        cout << "all passengers to get off, the " << bus.name_ << " is empty." << endl;
    }
}

void get_on_off_thd(Bus &bus)
{
    char c;
    while (cin >> c)
    {
        switch (c)
        {
        case 'q':
            {
                cout << " the "<< bus.name_ << " is going to offline"<<endl;
                bus.offline_ = true;
            }
            break;
        case 'a':
            {
                bus.get_on();
            }
            break;
        
        case 'l':
            {
                bus.get_off();
            }
            break;
        
        default:
            std::this_thread::sleep_for(std::chrono::seconds(1));
            cout << "input error , wait 1 second" << endl;
            break;
        }
        if(c=='q'){

            break;
        }
    }
    
}

int main()
{
    Bus bus_351("351");
    thread thd_departure(&departure_thd, std::ref(bus_351));
    thread thd_get_on_off(&get_on_off_thd, std::ref(bus_351));

    thd_departure.join();
    thd_get_on_off.join();
    return 0;
}

#endif



如果等待一次下车呢?

如果等待线程只需要等待一次,当条件为true时就不需要再等待这个条件变量了,这时条件变量未必就是最佳的选择了。
公交车发车了,假设你就坐在车上,1个小时后到达目的地,中间你可以做些别的事情消磨时间,你只需要等车到站这个信号就好,而且你只需等待一次,因为下次谁知道你要坐车去哪里。c++标准库使用future来处理这类一次性事件。

#if 0

/*

*/
class Person
{
public:
    Person(string name) : name_(name) {}
    bool Arrived()
    {
        std::this_thread::sleep_for(std::chrono::seconds(3));
        cout << name_ << ": arrived, please get off ... " << endl;
        return true;
    }
    void DoSomething()
    {
        cout << name_ << ": reading book ... " << endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaomin("xiaoming");
    std::future<bool> is_arrived = std::async(std::bind(&Person::Arrived, &xiaomin));

    cout << " waiting for ... " << endl;
    xiaomin.DoSomething();
    is_arrived.wait();
    return 0;
}

#endif

车上看会书, 嗯… 我看到哪了?

小明有个在公交车上读书的好习惯,下车前小明一般会保存下当前的读书进度,下次就不用从头开始翻了。

#if 0

class Person
{
public:
    Person(string name) : name_(name) {}
    string ReadBook()
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));
        cout << name_ << ": arrived, please get off ... " << endl;
        return "30%";
    }
    void DoSomething()
    {
        std::this_thread::sleep_for(std::chrono::microseconds(3000*1000+8888));
        cout << name_ << ": reading book ... " << endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaomin("xiaoming");
    auto start = std::chrono::high_resolution_clock::now();

    //std::launch::deferred 表示is_arrived.get()被调用时才开始读书,明显是不对的
    // std::future<string> is_arrived = std::async(std::launch::deferred,std::bind(&Person::ReadBook, &xiaomin));

    //std::launch::async(默认值,可不填) 在std::async调用时立即执行
    std::future<string> is_arrived = std::async(std::launch::async,std::bind(&Person::ReadBook, &xiaomin));
    cout << " waiting for ... " << endl;
    xiaomin.DoSomething();
    string rate_of_progress = is_arrived.get();

    auto stop = std::chrono::high_resolution_clock::now();
    cout << "read : " << rate_of_progress;

    //这个高精度计时器只能精确到秒?
    cout << " cost time = " <<std::chrono::duration<float>(stop-start).count() << " seconds"<<endl;
    return 0;
}

#endif

憋不住了,再过5秒,下车!

上车前小明犹豫了下要不要上个厕所,但发车前最终还是没有去,车开了。今天这趟车异常颠簸,晃晃悠悠,小明觉得自己的膀胱要爆炸了,
可还是迟迟未到站,最终小明决定,再等5秒钟,无论到哪里他都要下车了。

#if 0

class Person
{
public:
    Person(string name) : name_(name) {}
    string ReadBook()
    {
        std::this_thread::sleep_for(std::chrono::seconds(10));
        cout << name_ << ": arrived, please get off ... " << endl;
        return "30%";
    }
    void DoSomething()
    {
        std::this_thread::sleep_for(std::chrono::seconds(3));
        cout << name_ << ": reading book ... " << endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaomin("xiaoming");

    std::future<string> is_arrived = std::async(/*std::launch::deferred,*/std::bind(&Person::ReadBook, &xiaomin));
    cout << " waiting for .. " << endl;
    xiaomin.DoSomething();
    std::chrono::milliseconds span (1000*5);
    if(is_arrived.wait_for(span) == std::future_status::timeout)
    {
        cout << "It's time to get off -> xu_xu" <<endl;
    }

    //主程序都执行完毕了,居然还要等async, 默认是要join的吗?std::launch::deferred参数就不会等ReadBook线程退出了
    cout << "exit" << endl;
    return 0;
}

#endif

平行宇宙的中,另一个小明

平行宇宙是存在的。另外一个宇宙中,上车前小明犹豫了下要不要上个厕所,但最终还是在发车前去厕所解决了下,但这个宇宙中治安情况不是很好,
公交车上经常有小偷出没,为了防止钱包被偷,小明不得不每1秒钟检查下…


#if 0
/*
    
*/
class Person
{
public:
    Person(string name) : name_(name) {}
    string ReadBook()
    {
        std::this_thread::sleep_for(std::chrono::seconds(10));
        cout << name_ << ": arrived, please get off ... " << endl;
        return "30%";
    }
    void DoSomething()
    {
        std::this_thread::sleep_for(std::chrono::seconds(3));
        cout << name_ << ": reading book ... " << endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaomin("xiaoming");

    std::future<string> is_arrived = std::async(/*std::launch::deferred,*/std::bind(&Person::ReadBook, &xiaomin));
    cout << " waiting for ... " << endl;
    std::chrono::milliseconds span (1000);
    while (is_arrived.wait_for(span) == std::future_status::timeout)
    {
        cout << "check wallet..." <<endl;
    }
    string rate_of_progress = is_arrived.get();
    cout << "read : " << rate_of_progress << endl;
    return 0;
}

#endif

小明,妈妈喊你吃饭啦

除了使用 future<type> 被动的等待任务的完成,promiss<type>提供了一种更加主动的方式:
使用promise<void>在线程之间同步信号
小明终于到家了,妈妈还在做晚饭,妈妈对小明说,你先回房间休息,饭好了我叫你。


#if 0
/*
    
*/
class Person
{
public:
    Person(string name) : name_(name) {}
    void DoCooking(std::promise<void> &p)
    {
        cout << name_ << ": please wait for dinner ... " << endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));
        cout << name_ << ": dinner is ready" << endl;
        p.set_value();
    }

    void DoRest(std::future<void> &p)
    {
        cout << name_ << ": rest and wait for dinner " << endl;

        //小明太饿了,每过1s就要问一次妈妈饭好没好
        std::chrono::milliseconds span(1000);
        while (p.wait_for(span) == std::future_status::timeout)
        {
             cout << name_ << ": mama, is dinner ready?" << endl;
        }
        p.get();
        cout << name_ << ": Ok, I'm comming" << endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaomin("xiaoming");
    Person mama("mama");

    promise<void> p1;
    std::future<void> p1_future = p1.get_future();
    std::thread cooking(std::bind(&Person::DoCooking, &mama, std::move(p1)));

    std::thread waitting(std::bind(&Person::DoRest, &xiaomin, std::move(p1_future)));

    waitting.join();
    cooking.join();

    return 0;
}
/*
mamaxiaoming: please wait for dinner ... : rest and wait for dinner 

xiaoming: mama, is dinner ready?
xiaoming: mama, is dinner ready?
xiaoming: mama, is dinner ready?
xiaoming: mama, is dinner ready?

mama: dinner is ready
xiaoming: Ok, I'm comming
*/

#endif

糟了,还有工作没完成,小花等等我

使用promise<type>在线程之间同步数据
晚饭后小明想起来今天还有工作没有完成,但是他的工作必须在小花完成后才能进行,于是他打电话给小花,小花说,你先等以下,
我完成后把文档发给你…

#if 0

class Person
{
public:
    Person(string name) : name_(name) {}
    void DoWork1(std::promise<string> &p)
    {
        cout << name_ << ": I'm working , wait a minute ... " << endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));
        cout << name_ << ": done!" << endl;
        string str = "what is the difference between 'I like you' and 'I love you'?";
        p.set_value(str);
    }

    void DoWork2(std::future<string> &f)
    {
        cout << name_ << ": xiaohua, I am waitting for you" << endl;
        string str2 = "When you like a flower you just pluck it;\nwhen you love a flower you water it daily.";

        string str1 = f.get();
        cout << name_ << ":good job, It's my turn. I merge the work\n\n" << str1<< "\n" << str2 << endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaoming("xiaoming");
    Person xiaohua("xiaohua");

    promise<string> p1;
    std::future<string> p1_future = p1.get_future();
    std::thread xiaohua_thd(std::bind(&Person::DoWork1, &xiaohua, std::move(p1)));
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::thread xiaoming_thd(std::bind(&Person::DoWork2, &xiaoming, std::move(p1_future)));

    xiaoming_thd.join();
    xiaohua_thd.join();
    

    return 0;
}
/*
xiaohua: I'm working , wait a minute ... 
xiaoming: xiaohua, I am waitting for you
xiaohua: done!
xiaoming:good job, It's my turn. I merge the work

what is the difference between 'I like you' and 'I love you'?
When you like a flower you just pluck it;
when you love a flower you water it daily.
*/

#endif

怎么还有监工,而且是两个监工?

std::future 不能复制,一个事件只能有一个futurestd::shared_future则可以复制,可以多个shared_future指向同一个事件
(比如用户页面多个地方都要响应同一个状态变化,就需要这样的特性)
比如:
小花完成工作后,小明开始干活了,这时老板打来了电话,要实时掌控工作进度,同时围观下工作过程,于是他找来一个萤石摄像头对准自己工作的
显示器, 这时他突然想起来老板娘也叮嘱他要围观工作现场,于是他又把老板娘也加入到分享中


#if 0

class Person
{
public:
    Person(string name) : name_(name) {}
    void DoWork(std::promise<string> &p)
    {
        cout << name_ << ". I'm  working..." << endl;
        string str2 = "When you like a flower you just pluck it;\nwhen you love a flower you water it daily.";

        string str1 = "what is the difference between 'I like you' and 'I love you'?";
        std::this_thread::sleep_for(std::chrono::seconds(6));
        p.set_value(str1+str2);
    }

    void Watch(std::shared_future<string>& s)
    {
        std::chrono::milliseconds span(2000);
        while(s.wait_for(span) == std::future_status::timeout){
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            cout << name_ << ": check work progress..." <<endl;
        }

        cout << name_<< ": 辛苦了!,get the result\n" << s.get()<< endl;
    }

private:
    string name_;
};

int main()
{
    Person xiaoming("xiaoming");
    Person laoban("laoban");
    Person laoban_niang("laoban niang");

    promise<string> p1;
    std::shared_future<string> s1_future = p1.get_future().share();
    std::shared_future<string> s2_future = s1_future;

    std::thread xiaoming_thd(std::bind(&Person::DoWork, &xiaoming, std::move(p1)));
    std::this_thread::sleep_for(std::chrono::milliseconds(1));//休眠1ms,减少打印错乱

    std::thread laoban_thd(std::bind(&Person::Watch, &laoban, std::move(s1_future)));
    std::this_thread::sleep_for(std::chrono::milliseconds(1));//休眠1ms,减少打印错乱

    std::thread laoban_niang_thd(std::bind(&Person::Watch, &laoban_niang, std::move(s2_future)));


    xiaoming_thd.join();
    laoban_thd.join();
    laoban_niang_thd.join();

    
    return 0;
}
/*
xiaoming. I'm  working...
laoban: check work progress...
laoban niang: check work progress...
laoban: check work progress...
laoban niang: check work progress...

laoban: 辛苦了!,get the result
what is the difference between 'I like you' and 'I love you'?When you like a flower you just pluck it;
when you love a flower you water it daily.

laoban niang: 辛苦了!,get the result
what is the difference between 'I like you' and 'I love you'?When you like a flower you just pluck it;
when you love a flower you water it daily.
*/

#endif

最后的考验

std::packaged_task<type> 类模板是比std::promise<type>类模板更高层次的抽象,可以将一个future绑定到一个函数或可调用对象
上,当std::packaged_task<type>对象被调用时,它就调用相关联的函数或可调用对象,并且让future就绪,将返回值作为关联数据存储
比如:
小明完成任务之后,终于松了一口气,睡觉前他看到了小花发来的信息:按顺序同时解出这4道题。小明一看,不屑地摇了
摇头,so easy, 我有万能任务处理器啊,什么样的任务搞不定?!

#if 1
/*
   
*/
class Person
{
public:
    Person(string name) : name_(name) {}
    ~Person(){}

    void StopSupperTaskMachine(){
        {
            std::unique_lock<std::mutex> lock(queue_mx_);
            stop_ = true;
        }
        queue_cv_.notify_all();
        for(std::thread &work : workers_){
            work.join();
        }
    }
    void StartSupperTaskMachine(int power){
        for(size_t i = 0;i<power;++i)
        workers_.emplace_back(
            [this]
            {
                for(;;)
                {
                    std::function<void()> task;

                    {
                        std::unique_lock<std::mutex> lock(this->queue_mx_);
                        this->queue_cv_.wait(lock,
                            [this]{ return this->stop_ || !this->tasks_.empty(); });
                            
                       if(this->stop_ && this->tasks_.empty())
                            return;
                        task = std::move(this->tasks_.front());
                        this->tasks_.pop();
                    }

                    task();
                }
            }
        );
    }

    template<class F, class... Args>
    auto PushTask(F&& f, Args&&... args) 
        -> std::future<typename std::result_of<F(Args...)>::type>
    {
        using return_type = typename std::result_of<F(Args...)>::type;

        auto task = std::make_shared< std::packaged_task<return_type()> >(
                std::bind(std::forward<F>(f), std::forward<Args>(args)...)
            );
            
        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mx_);
            tasks_.emplace([task](){ (*task)(); });
        }
        queue_cv_.notify_one();
        return res;
    }

   


private:
    string name_;
    bool stop_ = false;
    std::queue<function<void()>> tasks_;
    std::mutex queue_mx_;
    std::condition_variable queue_cv_;
    std::vector<std::thread> workers_;
};


string task1(){
    return "xiao";
}

auto task2 = [](){
    return "ming";
};

function<int(int,int)> task4=[](int a, int b){
    return a+b;
};


int main()
{
    Person xiaoming("xiaoming");

    xiaoming.StartSupperTaskMachine(4);
    auto f1 = xiaoming.PushTask(task1);
    auto f2 = xiaoming.PushTask(task2);

    auto f3 = xiaoming.PushTask([](){
        return 521;
    });

    auto f4 = xiaoming.PushTask(std::bind(task4,1300,14));
    cout << f1.get() << " " << f2.get() << " "<< f3.get() << " " << f4.get()<<endl;
    xiaoming.StopSupperTaskMachine();

    return 0;
}
/*
xiao ming 521 1314
*/

#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值