Files
how_cpp_work/coroutine/main.cpp

211 lines
5.5 KiB
C++
Raw Normal View History

2026-02-27 12:46:11 +08:00
#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>
#include <future>
#include <iostream>
namespace asio = boost::asio;
asio::awaitable<int> calculate_value()
{
asio::steady_timer timer(co_await asio::this_coro::executor);
timer.expires_after(std::chrono::seconds(1));
// 这里的 async_wait 是这样的:
// 1. 告诉操作系统:"1秒后叫醒我"
// 2. 操作系统内核开始计时
// 3. 你的线程继续做其他事
// 4. 1秒后,操作系统主动通知你的程序
// 所以这里在这 1 秒期间内,不占用任何CPU资源。
co_await timer.async_wait(asio::use_awaitable);
co_return 42;
/*
2025-12-16
线
*/
}
asio::awaitable<void> process_task()
{
try {
std::cout << "开始计算...\n";
int result = co_await calculate_value(); // 等待子协程
std::cout << "计算结果: " << result << "\n";
} catch (const std::exception& e) {
std::cerr << "计算错误: " << e.what() << "\n";
throw; // 重新抛出,让调用者知道
}
}
int test1()
{
asio::io_context io;
// 使用 use_future 获取 future
// co_spawn 只是安排协程,并无动作和运行。
std::future<void> fut = asio::co_spawn(io, process_task(), asio::use_future);
// 在工作线程中运行 io_context
std::thread io_thread([&io]() { io.run(); });
std::cout << "主线程等待协程完成...\n";
try {
// 等待协程完成
fut.get();
std::cout << "协程成功完成\n";
} catch (const std::exception& e) {
std::cerr << "协程异常: " << e.what() << "\n";
}
io_thread.join();
return 0;
}
// 这是一个阻塞版本的Worker
class Worker
{
public:
Worker() = default;
bool Run()
{
isOk_ = false;
if (!simulateCal()) {
return false;
}
std::cout << "Done..." << calData_ << std::endl;
return true;
}
private:
bool simulateCal()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
calData_ = 12;
return true;
}
private:
bool isOk_{};
int calData_{};
};
int testRun()
{
Worker wk;
return wk.Run();
}
// 上面阻塞版本的Worker改成协程版本
class WorkerCoroutine
{
public:
WorkerCoroutine(asio::io_context& io) : io_(io), pool_(std::thread::hardware_concurrency())
{
}
asio::awaitable<bool> Run()
{
isOk_ = false;
bool success = co_await simulateCal();
if (!success) {
co_return false;
}
std::cout << "Done..." << calData_ << std::endl;
isOk_ = true;
co_return true;
}
private:
asio::awaitable<bool> simulateCal()
{
// 核心代码在这里
// 从这里可以看出,协程只适合能通知唤醒的阻塞,比如 socket.recv 虽然阻塞
// 但是可以被 OS 唤醒。不适合直接的阻塞比如 从一个驱动中读取数组,然后驱动很慢有阻塞这种。
// 非要用的话,需要把真正的阻塞(不是睡眠那种阻塞而是真正的耗时阻塞,比如计算)包装成异步。
// 测试可唤醒异步
// asio::steady_timer timer(io_);
// timer.expires_after(std::chrono::seconds(3));
// co_await timer.async_wait(asio::use_awaitable);
// 测试包装后的真正阻塞
// auto f = std::async(std::launch::async, [this]() { return realBlock(); });
// 不能这么写。
// co_await f.get();
// auto ex = co_await asio::this_coro::executor;
// auto r = co_await asio::co_spawn(io_, readBlockCorouitine, asio::use_awaitable);
// 这个虽然能执行,但是实际只是因为这里只有一个协程。
// co_await readBlockCorouitine();
bool result = co_await asio::co_spawn(
pool_, [this]() -> asio::awaitable<bool> { co_return co_await readBlockCorouitine(); }, asio::use_awaitable);
calData_ = 12;
co_return true;
}
// 真正阻塞的函数
bool realBlock()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
return true;
}
asio::awaitable<bool> readBlockCorouitine()
{
std::cout << "模拟耗时开启..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "模拟耗时结束..." << std::endl;
co_return true;
}
private:
asio::io_context& io_;
bool isOk_{};
int calData_{};
asio::thread_pool pool_;
};
int testRunCoroutine()
{
asio::io_context io;
WorkerCoroutine wk(io);
// 使用 use_future 获取 future
std::future<bool> result_future = asio::co_spawn(io,
wk.Run(), // 注意:这里要传 wk.Run() 而不是 wk
asio::use_future);
// 在工作线程运行 io_context
std::thread io_thread([&io]() { io.run(); });
std::cout << "开始异步计算,主线程可以继续...\n";
bool result = false;
try {
// 等待异步结果
result = result_future.get();
} catch (const std::exception& e) {
std::cerr << "协程异常: " << e.what() << "\n";
}
io_thread.join();
return result ? 0 : 1; // 返回 0 表示成功
}
int main()
{
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
testRunCoroutine();
return 0;
}