211 lines
5.5 KiB
C++
211 lines
5.5 KiB
C++
#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;
|
|
} |