复习一下函数绑定:
int action(string s, int i, int v1, int v2, int v3)
{
cout << i << ":" << s << endl;
return s.size() + v1 + v2 + v3;
}
只是需要一个好多入参的函数而已,接下来造一个函数,用于绑定action和它的第1,3,4,5个入参:
void bind_action_and_parameters(std::string const& message)
{
int item_1, item_2, item_3;
cout << "请输入三个整数:";
cin >> item_1 >> item_2 >> item_3;
//开始绑
typedef std::function<int (string, int, int, int)> Action;
Action f = std::bind(action, placeholders::_1, item_1, item_2, item_3);
///典型用法:把绑定好的结果传递出去
foo(f);
}
不带“策略”入参的async()函数的入参列表是“(动作、入参1、入参2、入参3……)”,回头再看bind()函数的入参列表,二者是不是很像?二者都不立即执行动作,而是将动作和动作所需的数据打包;
主要区别在于,bind()在当前线程打包,并得到一个“包裹”对象,该对象可在当前线程或者交给其他线程执行;
async()则不关心绑定的细节,终点解决如何在当前线程得到其他线程执行某个“包裹”的结果,即“存根”。
如果async()调用时指定使用“拖延”策略,在效果上就更接近bind()了。
还有一个细节上的区别:由于bind()最终动作调用代码由我们自己写,因此在绑定时可以只绑定部分数据,其他数据用“placeholders::_1、placeholders::_2”等预定义的常量代替。
async()对操作的调用由标准库的作者实现,如果我们不给足数据,那边标准库要调用,它怎么补?
继续以“射击,枪,子弹,敌人”为例。bind()的情况是子弹上枪膛,枪挂在身上找敌人,找到后再瞄准射击;
async()的情况是子弹上枪膛,把枪交给一个小弟,交待他执行“找敌人,瞄准射击”的操作,而我们保留关注操作结果的权利和途径。
当然也可以在bind()之后,再通过async()交给另一个线程运行。当我们连到时是自己(当前线程)还是别人(其他线程)来执行这个动作都无法一下子做决定,可手上又已经有了部分该动作所需的数据时,就特别需要这两兄弟“强强联手”。
此处举一个“四强联手”的例子,多两个“强”是指lambda表达式和auto:
可以看到,若是同步执行,则在当前线程内执行,若是异步执行,则新开一个线程,在新的线程内执行。
不考虑例中选择“同步”执行的情况,将整件事简化为:在前面通过bind()绑定操作和数据,得到一个“包裹”对象,而后在需要时将该“包裹”传递给async()以实现异步调用。
对应的需求是:我想打包一个异步任务,但现在不执行,将来需要时再执行。
C++标准库使用一个很形象的名字packaged_task <T>,T是某种操作的类型。操作可以是函数、函数对象、lambda、bind()的结果或function <T>等。
int add(int i1, int i2) {return (i1 + i2);}
int main()
{
packaged_task <int(int, int)> pkg(add);
future <int> r = pkg.get_future();
pkg(10, 11);
cout << r.get() << endl;
return 0;
}
005行,创建一个“packaged_task(包裹任务)”。“包裹”中含有待执行的操作,本例为add()函数。“包裹”里没有包含操作所需要的入参,如果有需要,请结合bind()使用
007行,调用packaged_task <T>的get_future()方法,从“包裹”中得到一个“存根”。
008行,调用pkg()。其实pkg是一个对象变量,packaged_task<T>是类模板。重点是此处调用机制等同于不带“策略”版本的async()函数调用,同步异步皆可能。
010行,使用前面的“存根”对象查询、等待任务执行结果
有了bind(),邦德可以把枪和子弹装好,遇见敌人再开枪;加上packaged_task(),邦德就可以将子弹交给跟班,遇上紧急情况就让跟班开枪,那邦德忙什呢?《白话C++》后面章节,俄罗斯方块见