问题
我们有一些共享资源:内存池、线程不安全的 API,任您选择。我们希望通过 ASIO 链控制对所述资源的访问。所有访问资源的例程都应该在该链上运行。
我们还使用 C++20 协程并享受它们提供的顺序执行的错觉。
当访问共享资源时,我们希望使用 挂起协程co_await
,切换到受祝福的链,对资源执行任何操作,然后返回到其本机执行器上的协程。
注意事项:
- 我们不想使用这个“
dispatch
技巧”,因为人体工程学很糟糕,而且这是一个等待发生的竞争条件。
auto s1 = bind_executor(strand, asio::deferred);
co_await asio::dispatch(s1);
// Access shared resource
co_await asio::dispatch(asio::deferred);
- 我们不想将上述技巧包装在另一个技巧中
asio::awaitable
,并可能为不需要的操作分配一个协程框架(通过use_awaitable
)
目前的解决方案
这就是我今天早上破解的内容,显然它不是很好(不使用概念,不转发参数,不允许返回值等),但它说明了我的目的:
(神箭)
static std::atomic_int tid_gen;
thread_local int const tid = tid_gen++;
inline void out(auto const& msg) { std::print("T{:x} {}\n", tid, msg); }
template <typename F, typename Ex, typename CT>
auto async_run_on(F f, Ex ex, CT&& token) {
return asio::async_initiate<CT, void()>(
[](auto handler, F f, Ex ex) {
ex.dispatch(
[=, handler = std::move(handler)]() mutable {
std::invoke(f);
handler.get_executor().execute(std::move(handler));
},
asio::get_associated_allocator(ex));
},
token, std::move(f), ex);
}
asio::awaitable<void> f(auto strand) {
out("Main");
co_await async_run_on([](){ out("Strand"); }, strand, asio::deferred);
out("Main again");
}
int main() {
asio::io_context io;
asio::thread_pool tp(1);
co_spawn(io, f(make_strand(tp)), asio::detached);
io.run();
}
希望不言而喻的是,我们可以扩展它来构建始终在给定执行器上运行并返回到任何地方的可调用等待对象。
问题
这个用例应该如何工作?
我在ASIO方面很差,最差的。我是第一个遇到这个问题的可能性为零。这让我对我提出的任何解决方案都非常怀疑。协程和链是 ASIO 构建块,我不是 sehe 或 ChrisK,我不是弄清楚这些拼图如何组合在一起的人。
但谷歌、SO 和 ASIO 文档对此都保持奇怪的沉默。对于链给出的示例和对于协程给出的示例之间几乎没有交叉授粉。
我们是否应该co_await
每次都进行链绑定延迟操作,或者是否有一个我完全错过的自由函数?