文章目录
条件变量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
不能复制,一个事件只能有一个future
,std::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