1.参考资料
API Reference Document
2.学习目的
写一个可控的线程类,把一些时钟类,网络类,日志类,数据库类都继承于线程类,这样可以统一管理这些线程,也可以让这些类的操作不直接影响主流程。
3.开始学习
①类的作用
类模板 std::future
提供访问异步操作结果的机制:
- (通过 std::async 、 std::packaged_task 或 std::promise 创建的)异步操作能提供一个
std::future
对象给该异步操作的创建者。
- 然后,异步操作的创建者能用各种方法查询、等待或从
std::future
提取值。若异步操作仍未提供值,则这些方法可能阻塞。
- 异步操作准备好发送结果给创建者时,它能通过修改链接到创建者的
std::future
的共享状态(例如 std::promise::set_value )进行
②类的使用
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int test()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
return 100;
}
int main()
{
//-----------------------------------------------
//std::future的基本用法
//-----------------------------------------------
//使用std::packaged_task包装函数
std::packaged_task<int()> task1(&test);
//std::packaged_task可以获取future
std::future<int> f1 = task1.get_future();
//在线程上运行
std::thread(std::move(task1)).detach();
std::cout << "-----------wait 和get的用法--------------" << std::endl;
std::cout << "Waiting..." << std::endl;
std::cout << "阻塞3秒:";
//在这个位置会发生阻塞,直到产生结果
f1.wait();
//这里的f.get()与wait有类似的效果,
//当结果没有产生的时候会发生阻塞,
//有结果的时候会直接返回结果
std::cout << "res = " << f1.get() << std::endl;
//std::cout << "-----------wait_for的用法--------------" << std::endl;
//---------------------------------------------------
//wait_for和std::async的用法:
//在wait_for中加入超时时间,当执行完成或者到了超时时间
//的时候,wait_for跳出阻塞,返回对应的状态
//--------------------------------------------------
std::future<int> f2 = std::async(std::launch::async, &test);
//-------------------------------------------------
//std::future的三种状态
//future_status::deferred 要计算结果的函数仍未启动
//future_status::ready 结果就绪
//future_status::timeout 已经过时限
std::cout << "waiting..." << std::endl;
std::future_status status;
int i = 0;
do {
status = f2.wait_for(std::chrono::seconds(1));
i++;
std::cout << "阻塞" << i << "秒 :";
if (status == std::future_status::deferred) {
std::cout << "deferred" << std::endl;
} else if (status == std::future_status::timeout) {
std::cout << "timeout" << std::endl;
} else if (status == std::future_status::ready) {
std::cout << "ready!" << std::endl;
}
} while (status != std::future_status::ready);
std::cout << "res =" << f2.get() << std::endl;;
std::cout << "-----------wait_until的用法--------------" << std::endl;
//------------------------------------------------------
//wait_until的用法,加入等待时间,当等待时间结束或者执行完成
//的时候,wait_until跳出阻塞,返回对应的状态
//-----------------------------------------------------
std::packaged_task<int()> task3(&test);
std::future<int> f3 = task3.get_future();
std::thread(std::move(task3)).detach();
std::cout << "waiting..." << std::endl;
//等待时间是当前时间加1秒,
std::cout << "阻塞1秒:";
auto untilTime1 = std::chrono::system_clock::now() + std::chrono::seconds(1);
auto status1 = f3.wait_until(untilTime1);
if (status1 == std::future_status::deferred) {
std::cout << "deferred" << std::endl;
} else if (status1 == std::future_status::timeout) {
std::cout << "timeout" << std::endl;
} else if (status1 == std::future_status::ready) {
std::cout << "ready!" << std::endl;
}
//阻塞了1秒再加2秒应该出结果了
std::cout << "阻塞3秒:";
auto untilTime2 = std::chrono::system_clock::now() + std::chrono::seconds(2);
auto status2 = f3.wait_until(untilTime2);
std::cout << "res =" << f3.get() << std::endl;
}
4.总结
①比较容易忽略的坑
在连接的时候需要加上 -lpthread 如果不加入的话,虽然可以编译链接过,但是执行的时候会出问题。
②初始化
一共有三种初始化的方法,其中std::async代码最简洁,std::packaged_task相对复杂一些,另一种方法通过 std::promise 比较复杂,暂时没有看明白。后面有需求的时候研究一下。
③函数的使用
wait(),get(),wait_for()和wait_until()的调用都会被阻塞,wait()和get()是阻塞到完成操作,wait_for()是阻塞到设置的时间长度,wait_until()是阻塞到时间点。
④应用
可以通过future写一个单一线程,主线程可以把任务发放到这些单一线程上,这些线程在完成操作之后,把结果返给主线程,继续等待任务。