std::future
—C++17 多线程
std::future
C++标准程序库使用future来模拟这类一次性事件:若线程需等待某个特定的一次性事件发生,则会以恰当的方式取得一个future,它代表目标事件;接着,该线程就能一边执行其他任务(光顾机场茶座),一边在future上等待;同时,它以短暂的间隔反复查验目标事件是否已经发生(查看出发时刻表)。这个线程也可以转换运行模式,先不等目标事件发生,直接暂缓当前任务,而切换到别的任务,及至必要时,才回头等待future准备就绪。future可能与数据关联(如航班的登机口),也可能未关联。一旦目标事件发生,其future即进入就绪状态,无法重置。
总之,先保存一个事件(创建future
对象),在未来获取事件的结果(get
)
#pragma once
#include <future>
#include <iostream>
#include <format>
using namespace std;std::string get_answer_from_hard_question()
{std::this_thread::sleep_for(2s);cout << "id: " << std::this_thread::get_id << endl;return string("答案在这\n");
}
void do_something()
{std::this_thread::sleep_for(5s);cout << "id: " << std::this_thread::get_id << endl;cout << "结束任务\n";
}void start()
{std::future<std::string> the_ans = std::async(std::launch::async,get_answer_from_hard_question);do_something();std::cout << std::format("答案是:{}", the_ans.get());
}
创建future
对象的方法
#include <string>
#include <future>
struct X
{void foo(int,std::string const&);std::string bar(std::string const&);
};
X x;
auto f1=std::async(&X::foo,&x,42,"hello"); ⇽--- ①调用p->foo(42,"hello"),其中p的值是&x,即x的地址
auto f2=std::async(&X::bar,x,"goodbye"); ⇽--- ②调用tmpx.bar("goodbye"),其中tmpx是x的副本
struct Y
{double operator()(double);
};
Y y;
auto f3=std::async(Y(),3.141); ⇽--- ③调用tmpy(3.141)。其中,由Y()生成一个匿名变量,传递给std::async(),进而发生移动构造。在std::async()内部产生对象tmpy,在tmpy上执行Y::operator()(3.141)
auto f4=std::async(std::ref(y),2.718); ⇽--- ④调用y(2.718)
X baz(X&);
std::async(baz,std::ref(x)); ⇽--- ⑤调用baz(x)
class move_only
{
public:move_only();move_only(move_only&&)move_only(move_only const&) = delete;move_only& operator=(move_only&&);move_only& operator=(move_only const&) = delete;void operator()();
};
auto f5=std::async(move_only()); ⇽--- ⑥调用tmp(),其中tmp等价于std::move (move_only()),它的产生过程与③相似
按默认情况下,std::async()
的具体实现会自行决定——等待future
时,是启动新线程,还是同步执行任务。大多数情况下,我们正希望如此。不过,我们还能够给std::async()
补充一个参数,以指定采用哪种运行方式。参数的类型是std::launch
,其值可以是std::launch::deferred
或std::launch::async
。前者指定在当前线程上延后调用任务函数,等到在future
上调用了wait()
或get()
,任务函数才会执行;后者指定必须另外开启专属的线程,在其上运行任务函数。该参数的值还可以是std::launch::deferred | std::launch:: async
,表示由std::async()
的实现自行选择运行方式。最后这项是参数的默认值。若延后调用任务函数,则任务函数有可能永远不会运行。举例如下。
auto f6=std::async(std::launch::async,Y(),1.2); ⇽--- ①运行新线程auto f7=std::async(std::launch::deferred,baz,std::ref(x)); ⇽--- ②在wait()或get()内部运行任务函数
auto f8=std::async( ⇽--- std::launch::deferred | std::launch::async,baz,std::ref(x));
auto f9=std::async(baz,std::ref(x)); ⇽--- ③交由实现自行选择运行方式
f7.wait(); ⇽--- ④前面②处的任务函数调用被延后,到这里才运行