使用 Boost Asio,我将如何实现一个“事件”类来恢复 C++20 协程?
有点像这样:
// Oversimplicated, but hopefully good enough as example
struct oneshot_event {
void raise();
async::awaitable<void> wait();
};
在哪里
wait()
可以被co_await
ed并且协程一旦调用raise()
就恢复协程。
我知道 Boost Asio 有(或曾经有?我在过时的文档中找到了它)一个可以 yield 的
coro
类,但这对我没有帮助,因为有多个事件可以等待。我也不确定所有这些 Boost.asio
和 Boost.coroutine
的东西是如何一起工作的
吐息番
希望这就是你想要的:
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <iostream>
#include <memory>
namespace asio = boost::asio;
using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::use_awaitable;
// Struct representing an event that can be raised and waited upon
struct oneshot_event {
asio::io_context& io_; // Reference to the IO context for async operations
bool raised_ = false; // Flag indicating whether the event has been raised
std::vector<std::shared_ptr<asio::steady_timer>> timers_; // Timers associated with the event
// Constructor that takes an IO context reference
oneshot_event(asio::io_context& io) : io_(io) {}
// Raise the event, allowing waiting coroutines to resume
void raise() {
if (!raised_) {
raised_ = true;
for (auto& timer : timers_)
timer->cancel();
}
}
// Wait for the event to be raised
awaitable<void> wait() {
if (!raised_) {
// Create a new shared_ptr to a timer and add it to the vector
auto timer = std::make_shared<asio::steady_timer>(io_, std::chrono::seconds(0));
timers_.push_back(timer);
// Suspend the coroutine until the timer expires or is canceled
co_await timer->async_wait(use_awaitable);
}
}
};
// Coroutine that waits for an event and resumes when the event is raised
awaitable<void> myCoroutine(oneshot_event& event) {
std::cout << "Coroutine waiting...\n";
co_await event.wait();
std::cout << "Coroutine resumed!\n";
}
int main() {
asio::io_context io;
oneshot_event event(io);
auto coroutine = myCoroutine(event);
co_spawn(io, std::move(coroutine), detached);
std::cout << "Sleeping for 3 seconds...\n";
std::this_thread::sleep_for(std::chrono::seconds(3));
event.raise(); // Resume the coroutine
io.run();
return 0;
}
输出:
Sleeping for 3 seconds...
Coroutine waiting...
Coroutine resumed!
传统的答案会使用等待计时器(gera 发布了一个答案)。
但是,这可能容易出错且笨拙,尤其是对于多线程(两种竞争条件(例如 https://stackoverflow.com/a/22204127/85371,更具体地说 https://stackoverflow.com/a/43169596 /85371) 和数据竞争(例如 gera 的代码不是线程安全的)。
更灵活的是实验频道支持:https://www.boost.org/doc/libs/master/doc/html/boost_asio/overview/channels.html
这将允许您为多个事件使用多个通道,或者在单个通道上支持多个事件。通道以开箱即用的线程安全变体 (
asio::concurrent_channel
) 存在。