准备头文件
完整代码:
#include <iostream>
#include <string>
#include <sstream>
#include <ctime>
#include <random>
#include <chrono>
#include <future>
#include <thread>
#include <mutex>
using namespace std;
//加了锁的cout单例定义
struct COutWithMutex
{
public:
static COutWithMutex& Get()
{
static COutWithMutex instance;
return instance;
}
void PrintLn(string const& line)
{
//锁守护的作用范围,是这个代码块
lock_guard <mutex> g(_m);
cout << line << endl;
}
private:
/* 允许程序员明确在结构或类定义中指明这家伙的
默认初始化工作内容,就是什么也不做,
请编译器自行处理去吧。其方法是写上默认构造函数,
不实现它,只是将它声明为"default(默认的)" */
COutWithMutex() = default;
mutex _m;
};
/*接着是洗车过程。假设洗车需要五个步骤,
每个步骤所花费的时间随机生成,以示公平:*/
int get_desc_id(string const& desc)
{
int id = 0;
for(auto c : desc)
{
//求字符串中各字符的累加值。
id += static_cast<int>(c);
}
return id;
}
//返回所有洗车步骤的总用时。
int Washing(string const desc)
{
int total(0);
int id = get_desc_id(desc);
//以 time(nullptr) + id为种子,再使用数学算法计算出随机数
mt19937 rd(time(nullptr) + id);
for(int i = 0; i < 5; ++i)
{
// rd() % 10 产生的随机数在[1, 9]之间
int seconds = 1 + (rd() % 10);
stringstream ss;
ss << "[洗车房] - 正在执行清洗" << desc << "的第"
<< i + 1 << "道工序。预计需要" << seconds
<< "秒,累计已用" << total << "秒。";
COutWithMutex::Get().PrintLn(ss.str());
/* chrono::seconds是时间长度,秒, std::this_thread只是一个namespace,
并不是某个可以代表当前线程的对象。不过我们所需的,也只是
在当前线程调用相关休眠函数,然后让当前线程休眠,而不是让其他线程休眠*/
this_thread::sleep_for(chrono::seconds(seconds));
total += seconds;
/* 当前线程放弃执行,让操作系统调度另一线程继续执行 */
this_thread::yield();
}
stringstream ss;
ss << "[洗车房] - 完成清洗 " << desc
<< " 。累计使用 " << total << " 秒。";
COutWithMutex::Get().PrintLn(ss.str());
return total;
}
/* 等的就是前面“Washing()”所代表的“洗车”异步过程,
等完后取什么结果?取的就是"Washing()"的返回值,即特定洗车过程
所花费的总时长 */
//因为Washing()的返回值是int,所以shared_future的模板参数是int
void Watting(shared_future <int> f_self, shared_future <int> f_other
, char const* who, char const* action)
{
bool finished_self(false), finished_other(false);
bool lost = false;
future_status status_self, status_other;
for(;;)
{
//如果自己的车还未洗完
if(!finished_self)
{
/* 不强制启动目标过程,纯粹地等待上一段时间,
如果等的时候目标过程执行完毕,它欢快地回来告诉调用者
“异步过程”执行完毕,可以调用get()取结果啦”,
否则它应该是很沮丧的。 */
status_self = f_self.wait_for(chrono::seconds(1));
//如果status_self==ready,则finished_self为true。
finished_self = (status_self == future_status::ready);
}
if(!finished_other)
{
status_other = f_other.wait_for(chrono::seconds(1));
finished_other = (status_other == future_status::ready);
}
stringstream ss;
ss << "[" << who << "]-";
//如果自己洗完了,别人还没洗完
if(finished_self && !finished_other)
{
//得到自己的洗车结果(用时)
int seconds = f_self.get();
ss << "哈哈哈!谢谢店家看得起!回头给五星好评。"
<< "这位兄弟," << seconds << " 秒洗车的境界,有空来交流。"
<< "您慢慢品茶,我有个上千万的项目要忙,告退。";
COutWithMutex::Get().PrintLn(ss.str());
break;
}
//如果都洗完了
if(finished_self && finished_other)
{
int seconds = f_other.get() - f_self.get();
if(seconds > 0)
{
ss << "店家,这 " << -seconds << " 秒颜面损失无价,你赔我!";
}
else //seconds == 0
{
ss << "这么巧,走了!";
}
COutWithMutex::Get().PrintLn(ss.str());
break;
}
//自己没洗完,别人洗完了
if(!finished_self && finished_other && !lost)
{
lost = true;
int seconds = f_other.get(); //别人洗车的时间
ss << "(" << action << ")店家你别急,豪车就需慢慢洗。"
<< " 那位兄弟,洗个车才坚持" << seconds << "秒?路上小心。";
COutWithMutex::Get().PrintLn(ss.str());
continue;//继续for循环
}
ss << "我喝口茶,再等等。";
COutWithMutex::Get().PrintLn(ss.str());
}
}
int main()
{
/* 两次异步调用结果返回shared_future<T>,这样说是不准确的,
因为asyn()函数只能返回future<T>对象,只不过它能
进一步转换为此时我们所需要的shared_future<T>。为此,
这里不能使用auto简化f_wash_zhang和f_wash_li的类型声明,
倒也不是没有办法,
可以写成 auto sf = async(...).share() //"..."是省略的入参*/
/* 普通future就是一个收条只能用一次(钱还了,借条也请作废),
而shared_future则是一个收条可以用多次 */
//洗车结果,两家都关心,所以使用shared_future
shared_future <int> f_wash_zhang =
async(launch::async, Washing, "~张家 - 宝牛车 ~ ");
shared_future <int> f_wash_li =
async(launch::async, Washing, "~李家 - 倔驴车 ~");
/* 又是一对async()调用,不过这次不存在“多处共同获取”两人等待过程的需要,
另外也没有指定异步过程启动策略。管他两呢,爱等不等。*/
auto f_wait_zhang = async(Watting, f_wash_zhang, f_wash_li
, "张先生", "掏出枪形打火机点了颗烟"); //等待结果
auto f_wait_li = async(Watting, f_wash_li, f_wash_zhang
, "李先生", "拿着刀伸进嘴里剔了剔牙");
auto zero = chrono::seconds(0);
while(f_wait_zhang.wait_for(zero) != future_status::ready
|| f_wait_li.wait_for(zero) != future_status::ready)
{ // wait_for()不强制启动目标过程,只是纯粹登上一段时间
this_thread::yield(); //线程切换
// cout << "线程yield ";
}
cout << "[扫地阿姨] - 两(kuai)位(dian)慢(gun)走(dan)!" << endl;
return 0;
}