future和promise可以配合使用,主要作用是在不同线程间传递数据。举个简单例子,直接看代码:
#include <iostream>
#include <future>
#include <thread>
#include <unistd.h>
void set_promise(std::promise<int>& p) {
std::cout << "set_promise begin." << std::endl;
sleep(1);
p.set_value(100);
std::cout << "set_promise end." << std::endl;
}
int main() {
std::promise<int> p;
// 将promise和future绑定,这一步就是允诺future,未来会有人对promise赋值
std::future<int> f = p.get_future();
std::thread t(&set_promise, std::ref(p));
// 通过get 拿到promise set的value
std::cout << f.get() << std::endl;
t.join();
return 0;
}
从这个代码可以看到主线程中声明了一个p,然后通过p.get_future()将promise和future绑定。再将promise变量传到set_promise线程中,在set_promise线程中对promise赋值。然后就可以在主线程中通过future的get获取promise设置的值。
运行结果:
x123@ubuntu:~/test$ ./test
set_promise begin.
set_promise end.
100
这里需要注意的一点是。如果set_promise线程迟迟没有对promise设置值,那主线程的future get会阻塞住。
其实这种方式完全可以应用在发送接收消息的案例中,下面可以举个apollo中实际应用promise和future的例子。apollo cyber中在client 发送和接收消息就是使用了promise和future。
可以简单看下apollo这块代码,完整代码大家可以从github上搜apollo获取。
template <typename Request, typename Response>
typename Client<Request, Response>::SharedFuture
Client<Request, Response>::AsyncSendRequest(SharedRequest request,
CallbackType&& cb) {
if (IsInit()) {
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
sequence_number_++;
transport::MessageInfo info(writer_id_, sequence_number_, writer_id_);
request_transmitter_->Transmit(request, info);
SharedPromise call_promise = std::make_shared<Promise>();
// 将future和call_promise绑定
SharedFuture f(call_promise->get_future());
pending_requests_[info.seq_num()] =
std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
return f;
} else {
return std::shared_future<std::shared_ptr<Response>>();
}
}
可以看到这里在发送请求的时候,就是声明了一个call_promise和一个future,并且通过call_promise->get_future()将future和promise绑定起来。然后再塞到pending_requests中,这里你可以理解AsyncSendRequest就是在线程1中执行,那promise的值在哪里设置?继续看下面代码:
template <typename Request, typename Response>
void Client<Request, Response>::HandleResponse(
const std::shared_ptr<Response>& response,
const transport::MessageInfo& request_header) {
ADEBUG << "client recv response.";
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
if (request_header.spare_id() != writer_id_) {
return;
}
uint64_t sequence_number = request_header.seq_num();
if (this->pending_requests_.count(sequence_number) == 0) {
return;
}
// 从pending_requests_中 获取call_promise和future
auto tuple = this->pending_requests_[sequence_number];
auto call_promise = std::get<0>(tuple);
auto callback = std::get<1>(tuple);
auto future = std::get<2>(tuple);
this->pending_requests_.erase(sequence_number);
// 给call_promise设置上value
call_promise->set_value(response);
// 触发callback
callback(future);
}
HandleResponse你可以理解也是个callback,是在别的线程中触发,暂时理解HandleResponse在线程2中触发吧,这里主要是拿到call_promise和future,并且给call_promise设置上value。那callback future后在哪里get呢? 继续看:
template <typename Request, typename Response>
typename Client<Request, Response>::SharedResponse
Client<Request, Response>::SendRequest(SharedRequest request,
const std::chrono::seconds& timeout_s) {
if (!IsInit()) {
return nullptr;
}
auto future = AsyncSendRequest(request);
if (!future.valid()) {
return nullptr;
}
auto status = future.wait_for(timeout_s);
if (status == std::future_status::ready) {
return future.get();
} else {
return nullptr;
}
}
这里在拿到future后,就可以直接通过future.get()获取刚才在线程2中设置的value值。
感兴趣的可以到github上看下apollo这块关于client封装的源码。