用入参的方式从某个线程中,取回处理结果
多了一个入参r,而且是引用方式
编译,会报一大串错误内容,大意是找不到类型为“void(int, int, int)”的函数,原因是test()函数中,我们传递第三个入参r时,没有按calc的要求使用引用,
这时2011年新标的std::ref小工具就派上用场了,
23行,创立了一个线程,
28行,代用trd.join()之后才访问r,以确保线程目标过程确实完成对r的处理。
【危险】:跨线程传递引用对象
使用引用传递对象,自然需要非常小心引用对象的生命周期,因此有时候也会改用指针,当然通常我们使用智能指针。
就算如此,相比高层概念“async/future”的功能,直接使用底层概念thread处理业务逻辑,还有一个大问题没解决:
线程目标函数执行过程中发生异常时,外部调用线程怎么捕获?
第一种做法当然又是“一不做二不休”。我们在线程目标过程中处理完所有异常,保证不向“外太空”抛出异常。
第二种做法是使用标准库的promise工具,然后将该问题转由future来解决。
promise直译就是“承诺”
两个巨大的数相加之后,造成一处,应该抛出异常,如何捕获?
通常我们不直接修改现有函数,而是在它的基础上加一层“承诺(promise)”,得到一个带承诺版本的操作:
std::promise<T> 就是T带上承诺以后的类型,依据需要,这里是int类型,当一切顺利,通过p.set_value(V)以设置正常结果;如果发生异常,通过set_exception(E)设置异常。
这里有个偷懒行为:我们使用“...”捕获任意类型异常,然后通过C++11标准库提供的current_exception()获得当前线程最后一次发生的异常
test()函数中,新线程的目标过程,改为使用calc_with_promise,对应的,第三个入参改为使用带承诺的版本:
请注意代码58行,detach()方法调用,使用“承诺”之后,最终是在future对象的get()时等待,因此可以不管线程对象是否执行完毕。当然线程对象的生命周期在取得结果之前,必须有保障。
最后看主函数:
运行结果:
从“线程”小节开始折腾,一直到“承诺”,感觉就是证明了一个定理:thread + promise = async。
这个定理不太严谨,但却再次强调了C++11新表并发编程的一个原则:
一般的并发问题,使用“异步”实现已经很够用了。