异步 API 中 std::promise 的生命周期问题
Lifetime issues of std::promise in an async API
我想知道如何使用 promises 和 futures 开发异步 API。
该应用程序使用单个数据流,用于主动提供的周期性数据和 requesty/reply 通信。
因为 requesty/reply 在收到回复之前阻塞不是一个选项,我不想使用回调乱扔代码,所以我想写一些接受预期回复的 ID,仅在收到后退出。由来电者阅读回复。
候选人API可能是:
std::future<void> sendMessage(Message msg, id expected)
{
// Write the message
auto promise = make_shared<std::promise<void>>();
// Memorize the promise somewhere accessible to the receiving thread
return promise->get_future();
}
收到消息后的工作线程应该能够查询数据结构以了解是否有人在等待它以及"release"未来。
鉴于承诺不可重用,我想了解的是我应该使用哪种数据结构来管理 "in flight" 承诺。
此答案已被重写。
设置共享标志的状态可以让工作人员知道另一方,比如老板,是否仍在期待结果。
共享标志连同承诺和未来可以包含在 class(模板)中,例如 Request。老板通过销毁他的请求副本来设置标志。并且工作人员通过在他自己的请求副本上调用某个成员函数来查询老板是否仍然希望完成请求。
flag上的同步reading/writing应该是同步的吧
老板可能无法访问承诺,工人可能无法访问未来。
请求最多应该有两个副本,因为标志将在请求对象的销毁时设置。为此,我们可以delcare相应的成员函数为delete
或private
,并提供两份构造请求。
下面是请求的简单实现:
#include <atomic>
#include <future>
#include <memory>
template <class T>
class Request {
public:
struct Detail {
std::atomic<bool> is_canceled_{false};
std::promise<T> promise_;
std::future<T> future_ = promise_.get_future();
};
static auto NewRequest() {
std::unique_ptr<Request> copy1{new Request()};
std::unique_ptr<Request> copy2{new Request(*copy1)};
return std::make_pair(std::move(copy1), std::move(copy2));
}
Request(Request &&) = delete;
~Request() {
detail_->is_canceled_.store(true);
}
Request &operator=(const Request &) = delete;
Request &operator=(Request &&) = delete;
// simple api
std::promise<T> &Promise(const WorkerType &) {
return detail_->promise_;
}
std::future<T> &Future(const BossType &) {
return detail_->future_;
}
// return value:
// true if available, false otherwise
bool CheckAvailable() {
return detail_->is_canceled_.load() == false;
}
private:
Request() : detail_(new Detail{}) {}
Request(const Request &) = default;
std::shared_ptr<Detail> detail_;
};
template <class T>
auto SendMessage() {
auto result = Request<T>::NewRequest();
// TODO : send result.second(the another copy) to the worker
return std::move(result.first);
}
新请求由factroy函数NewRequest
构造,return值是一个std::pair
,其中包含两个std::unique_ptr
,每个都持有一份新创建的请求.
工作人员现在可以使用成员函数CheckAvailable()
检查请求是否被取消。
共享状态由 std::shared_ptr 妥善管理(我相信)。
关于std::promise<T> &Promise(const WorkerType &)
的注意事项:常量引用参数(应根据您的实现将其替换为适当的类型)是为了防止老板意外调用此函数,而工人应该能够轻松为调用此函数提供适当的参数。 std::future<T> &Future(const BossType &)
.
也一样
我想知道如何使用 promises 和 futures 开发异步 API。 该应用程序使用单个数据流,用于主动提供的周期性数据和 requesty/reply 通信。
因为 requesty/reply 在收到回复之前阻塞不是一个选项,我不想使用回调乱扔代码,所以我想写一些接受预期回复的 ID,仅在收到后退出。由来电者阅读回复。
候选人API可能是:
std::future<void> sendMessage(Message msg, id expected)
{
// Write the message
auto promise = make_shared<std::promise<void>>();
// Memorize the promise somewhere accessible to the receiving thread
return promise->get_future();
}
收到消息后的工作线程应该能够查询数据结构以了解是否有人在等待它以及"release"未来。
鉴于承诺不可重用,我想了解的是我应该使用哪种数据结构来管理 "in flight" 承诺。
此答案已被重写。
设置共享标志的状态可以让工作人员知道另一方,比如老板,是否仍在期待结果。
共享标志连同承诺和未来可以包含在 class(模板)中,例如 Request。老板通过销毁他的请求副本来设置标志。并且工作人员通过在他自己的请求副本上调用某个成员函数来查询老板是否仍然希望完成请求。
flag上的同步reading/writing应该是同步的吧
老板可能无法访问承诺,工人可能无法访问未来。
请求最多应该有两个副本,因为标志将在请求对象的销毁时设置。为此,我们可以delcare相应的成员函数为delete
或private
,并提供两份构造请求。
下面是请求的简单实现:
#include <atomic>
#include <future>
#include <memory>
template <class T>
class Request {
public:
struct Detail {
std::atomic<bool> is_canceled_{false};
std::promise<T> promise_;
std::future<T> future_ = promise_.get_future();
};
static auto NewRequest() {
std::unique_ptr<Request> copy1{new Request()};
std::unique_ptr<Request> copy2{new Request(*copy1)};
return std::make_pair(std::move(copy1), std::move(copy2));
}
Request(Request &&) = delete;
~Request() {
detail_->is_canceled_.store(true);
}
Request &operator=(const Request &) = delete;
Request &operator=(Request &&) = delete;
// simple api
std::promise<T> &Promise(const WorkerType &) {
return detail_->promise_;
}
std::future<T> &Future(const BossType &) {
return detail_->future_;
}
// return value:
// true if available, false otherwise
bool CheckAvailable() {
return detail_->is_canceled_.load() == false;
}
private:
Request() : detail_(new Detail{}) {}
Request(const Request &) = default;
std::shared_ptr<Detail> detail_;
};
template <class T>
auto SendMessage() {
auto result = Request<T>::NewRequest();
// TODO : send result.second(the another copy) to the worker
return std::move(result.first);
}
新请求由factroy函数NewRequest
构造,return值是一个std::pair
,其中包含两个std::unique_ptr
,每个都持有一份新创建的请求.
工作人员现在可以使用成员函数CheckAvailable()
检查请求是否被取消。
共享状态由 std::shared_ptr 妥善管理(我相信)。
关于std::promise<T> &Promise(const WorkerType &)
的注意事项:常量引用参数(应根据您的实现将其替换为适当的类型)是为了防止老板意外调用此函数,而工人应该能够轻松为调用此函数提供适当的参数。 std::future<T> &Future(const BossType &)
.