前言
在异步编程中,各种回调将让人眼花缭乱,代码分散,维护起来十分困难。boost和C++11 的 future/promise 提供了一个很好的解决方案,使得代码更加漂亮、易维护。
在工作中,我也用过几次future/promise,但是还是十分生疏,所以决定学习下它的原理,用起来才更加顺畅。
查了很多资料,发现很多语言都有这个机制,但是关于C++的promise的资料却很少,只有一些使用的教程,而没有找到原理方面的。
“源码之前,了无秘密。”
所以还是决定从源码入手学习!
本文针对的源码是借鉴boost实现的版本,由于copyright的原因,就不贴出完整的源码了,需要学习的朋友可以参见boost的实现。
关于future/promise的简介,参见我之前写的一篇博文:
http://blog.youkuaiyun.com/jiange_zh/article/details/51602938
一、基于Future/Promise的异步同步化编程依赖的组件
- bind和callback,类似与boost库的bind和function;
- shared_ptr、scoped_ptr、tuple、exception,参考boost库中的shared_ptr、scoped_ptr、tuple、exception实现;
- Future和Promise,借鉴boost库的future设计思想;
- when_all,通过Future、Promise、tuple来实现,针对异步并行的同步化。
二、Function和Bind的用法
参见我之前写的一篇博文:
http://blog.youkuaiyun.com/jiange_zh/article/details/51598580
下面提到的bind类似于boost的bind,而Callback类似于Function,一般跟bind搭配使用。
三、shared_ptr、scoped_ptr、tuple
shared_ptr:引用计数。
scoped_ptr:不可转移所有权。
Tuple:很多的时候我们需要为函数返回多个值,我们可以使用class或struct来封装要返回的多个值,然后返回封装struct或class,但是使用这种方法的弊端就是增加的程序的代码量,最好是能通过一种匿名的struct或class来解决这个问题。Boost::tuple就为我们提供了一种类似于匿名struct的方法为我们解决函数的多个返回值的问题。既增强了代码的可读性又不增加代码量。其实std::pair就是boost::tuple的2个参数的特例,对boost::tuple你可以绑定更多的参数,或者你可以迭代实现无限多参数的情况。
四、EnableSharedFromThis
使用boost库时,经常会看到如下的类:
class A:public enable_share_from_this<A>
在什么情况下要使类A继承enable_share_from_this?
使用场合 :当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。
我们就使类A继承enable_share_from_this,然后通过其成员函数 share_from_this()返回当指向自身的share_ptr。
以上有2个疑惑:
1.把当前类对象作为参数传给其他函数时,为什么要传递share_ptr呢?直接传递this指针不可以吗?
一个裸指针传递给调用者,谁也不知道调用者会干什么?假如调用者delete了该对象,而share_tr此时还指向该对象。
2.这样传递share_ptr可以吗?share_ptr< this >
这样会造成2个非共享的share_ptr指向一个对象,最后造成2次析构该对象。
boost官方文档中一个非常典型的例子:
http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html
部分代码:
1 class tcp_connection
2 : public boost::enable_shared_from_this<tcp_connection>
3 {
4 public:
5 typedef boost::shared_ptr<tcp_connection> pointer;
6
7 static pointer create(boost::asio::io_service& io_service)
8 {
9 return pointer(new tcp_connection(io_service));
10 }
11
12 tcp::socket& socket()
13 {
14 return socket_;
15 }
16
17 void start()
18 {
19 message_ = make_daytime_string();
20
21 boost::asio::async_write(socket_, boost::asio::buffer(message_),
22 boost::bind(&tcp_connection::handle_write, shared_from_this(),
23 boost::asio::placeholders::error,
24 boost::asio::placeholders::bytes_transferred));
25 }
26
27 private:
28 tcp_connection(boost::asio::io_service& io_service)
29 : socket_(io_service)
30 {
31 }
32
33 void handle_write(const boost::system::error_code& /*error*/,
34 size_t /*bytes_transferred*/)
35 {
36 }
37
38 tcp::socket socket_;
39 std::string message_;
40 };
类tcp_connection继承enable_share_from_this,在22行里,它的成员函数start(),通过share_from_this返回指向自身的share_ptr。
五、Future和Promise
5.1 简介
Promise对象可保存T类型的值,该值可被future对象读取(可能在另一个线程中),这是promise提供的同步的一种手段。
在构造promise时,promise对象可以与共享状态关联起来,这个共享状态可以存储一个T类型或者一个由std::exception派生出的类的值,并可以通过get_future来获取与promise对象关联的对象,调用该函数之后,