C++20 引入的协程特性,对 C++ 具有极其重要意义。这一特性能够帮助开发者在多任务场景下编写出可读性、可维护性更好的代码,提升复杂系统的开发效率和可维护性。
需要明确的是,协程不是框架,也不是现成的库,更无法自动解决问题。本质上,协程是一种底层的基础编程能力,其真正的价值,完全取决于开发者如何基于这一能力进行上层封装与实际应用。
接下来,我们将从实际开发中的痛点问题切入,逐步了解协程的特点、技术细节、以及如何应用。
1. 问题场景
假设我们要实现一个包含两个任务的程序,我们先看看会遇到什么问题。
任务 A:从网络读取数据
这是一个典型的 IO 操作,需要等待外部网络返回。
任务 B:处理本地业务逻辑
这是纯计算或简单逻辑处理,可以立刻完成。
#include <iostream>
#include <thread>
#include <chrono>
// 模拟耗时 I/O
void read_from_network()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "网络 I/O 结束" << std::endl;
}
// 一个可以立刻完成的任务
void do_other_work()
{
std::cout << "执行其他任务" << std::endl;
}
int main()
{
std::cout << "程序开始" << std::endl;
// 任务 1:等待外部事件(阻塞)
read_from_network();
// 任务 2:本可以马上执行
do_other_work();
std::cout << "程序结束" << std::endl;
}
从代码逻辑上看,这个程序是正确的,但执行效果并不理想。程序在执行任务 A 时,会因为等待网络返回而阻塞当前线程。在这 3 秒内,线程什么也做不了,任务 B 明明已经具备执行条件,却只能被迫等待。
造成这一问题的根本原因在于:普通函数一旦开始执行,就必须一路执行到返回为止。当函数内部需要等待外部事件时,只能通过阻塞线程的方式等待,执行权无法主动让出。
简言之:普通函数无法暂停和恢复。
为解决这类问题,C++20 引入了协程。简单来说,协程可以理解为一种可暂停/恢复的函数。协程在执行过程中,如果遇到需要等待外部条件的地方,可以先暂停当前执行,把执行权交还出去。当外部条件满足后,再从之前暂停的位置继续运行。正因为具备这种能力,协程特别适合用于以下两类场景。
IO 密集型多任务场景:当程序需要频繁等待外部 IO(如网络请求、磁盘读写、数据库查询)时,协程可以在等待期间不阻塞线程,让线程继续执行其他任务,从而提升任务处理效率。
惰性计算场景:当计算过程可以自然拆分为多个阶段,并且结果是按需消费的(如无限序列生成、大数据分批处理、流式计算),协程可以在产出一个阶段性结果后暂停执行,直到外部真正需要下一步时再继续。
2. 语法规则
我们从普通函数开始,将其逐步改造为能够支持暂停/恢复的协程函数。
2.1 coroutine_handle
C++ 协程可支持暂停/恢复,编译器为每一个协程提供了 std::coroutine_handle 工具用于管理协程,例如:创建协程、销毁协程、恢复协程、判断协程状态等等。
_EXPORT_STD template <class _CoroPromise>
struct coroutine_handle {
constexpr coroutine_handle() noexcept = default;
constexpr coroutine_handle(nullptr_t) noexcept {}
_NODISCARD static coroutine_handle from_promise(_CoroPromise& _Prom) noexcept { // strengthened
const auto _Prom_ptr = const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Prom)));
const auto _Frame_ptr = __builtin_coro_promise(_Prom_ptr, 0, true);
coroutine_handle _Result;
_Result._Ptr = _Frame_ptr;
return _Result;
}
coroutine_handle& operator=(nullptr_t) noexcept {
_Ptr = nullptr;
return *this;
}
_NODISCARD constexpr void* address() const noexcept {
return _Ptr;
}
_NODISCARD static constexpr coroutine_handle from_address(void* const _Addr) noexcept { // strengthened
coroutine_handle _Result;
_Result._Ptr = _Addr;
return _Result;
}
constexpr operator coroutine_handle<>() const noexcept {
return coroutine_handle<>::from_address(_Ptr);
}
constexpr explicit operator bool() const noexcept {
return _Ptr != nullptr;
}
_NODISCARD bool done() const noexcept { // strengthened
return __builtin_coro_done(_Ptr);
}
void operator()() const {
__builtin_coro_resume(_Ptr);
}
void resume() const {
__builtin_coro_resume(_Ptr);
}
void destroy() const noexcept { // strengthened
__builtin_coro_destroy(_Ptr);
}
_NODISCARD _CoroPromise& promise() const noexcept { // strengthened
return *reinterpret_cast<_CoroPromise*>(__builtin_coro_promise(_Ptr, 0, false));
}
private:
void* _Ptr = nullptr;
};
当创建一个协程的时候,std::coroutine_handle 会在内部创建一块存储空间,用于保存协程所需要的所有状态,这块存储空间称作协程帧。很显然,当协程销毁时,需要释放这块存储空间。为了避免大家忘记释放,C++ 要求我们要通过 RAII 的方式使用 std::coroutine_handle,即:创建一个 RAII 对象,在构造函数中创建 std::coroutine_handle,在析构函数中销毁 std::coroutine_handle。
#include <iostream>
#include <coroutine>
struct CoroRAII
{
std::coroutine_handle<> handle;
// 构造函数初始化 handle 对象
CoroRAII(std::coroutine_handle<> handle)
{
this->handle = handle;
}
// 封装其他 handle 操作
void resume()
{
if (!handle.done())
{
handle.resume();
}
}
// 析构函数初始化 handle 对象
~CoroRAII()
{
if (handle)
{
handle.destroy();
}
}
};
CoroRAII my_coroutine()
{
std::cout << "协程执行" << std::endl;
}
int main()
{
CoroRAII coro = my_coroutine();
return 0;
}
注意:我们并不能够在 CoroRAII 直接创建一个有效的 std::coroutine_handle 对象,原因有二:
std::coroutine_handle必须基于_CoroPromise构建,而_CoroPromise目前不知道是什么。CoroRAII必须由编译器创建,可编译器无法知道如何构建CoroRAII对象。
2.2 promise_type
在 CoroRAII 中,我们需要定义 struct promise_type,它就是构造 std::coroutine_handle 所需要的对象。这个对象作用是什么?
- 它定义了:协程创建后,是先挂起,还是直接执行?
- 它定义了:当协程中有未处理的异常时,怎么处理?
- 它定义了:当协程中返回结果如何处理?
- 它定义了:当协程逻辑结束是马上销毁,还是稍后由用户销毁?
- 它定义了:
CoroRAII对象如何构建【重要】
#include <iostream>
#include <coroutine>
struct CoroRAII
{
struct promise_type
{
// 创建协程操作管理对象
CoroRAII get_return_object()
{
return CoroRAII{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
// 决定协程创建后是否马上执行
// std::suspend_never:不挂起
// std::suspend_always:挂起
std::suspend_always initial_suspend()
{
return {};
}
// 协程执行完后是否马上销毁
// std::suspend_never:不挂起,立即销毁资源
// std::suspend_always:挂起,不立即销毁,等待外部 destroy 释放资源
std::suspend_always final_suspend() noexcept
{
return {};
}
// 协程中有未处理的异常时如何处理
void unhandled_exception()
{
std::cout << "未处理的异常" << std::endl;
}
};
std::coroutine_handle<> handle;
CoroRAII(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
void resume()
{
if (!handle.done())
{
handle.resume();
}
}
~CoroRAII()
{
if (handle)
{
handle.destroy();
}
}
};
CoroRAII my_coroutine()
{
std::cout << "协程执行" << std::endl;
}
int main()
{
CoroRAII coro = my_coroutine();
return 0;
}
到这里,需要同学们注意下协程的创建流程,编译器会:
- 首先,创建 promise_type 对象
- 然后,调用 promise_type::get_return_object() 函数返回协程管理对象
- 接着,调用 promise_type::initial_suspend() 函数确定协程初始化后是否马上执行
- 如果,协程中抛出了未处理的异常,promise_type::unhandled_exception() 函数会被执行处理异常
- 最后,协程结束之后,调用 promise_type::final_suspend() 函数来确定马上销毁协程,还是用户手动销毁
从语法角度,编译器还是无法确定 my_coroutine 是普通函数,还是协程函数。所以,编译器又做了一个要求,对于协程函数 my_coroutine,内部必须出现协程关键字:co_return、co_yield、或者 co_await。
我们总结一下,在 C++20 中,一个函数被编译器判定为协程,需同时满足以下两个核心条件:
- 函数内部要求:函数体内部必须显式使用
co_return、co_yield、co_await中的至少一个协程关键字。
- 返回类型要求:返回类型中必须嵌套定义合法的
promise_type结构体,缺失会编译报错。
2.3 co_return
co_return 可以直接结束协程逻辑、或者返回一个值再结束协程逻辑,当协程逻辑结束后,会自动调用 promise_type::final_suspend 函数 。
注意:协程逻辑结束并不代表协程对象立即销毁,协程对象是否随着协程逻辑结束而销毁是由 final_suspend 的返回值决定。
std::suspend_never→ 协程立即销毁,资源释放std::suspend_always→ 协程挂起,由外部显式调用destroy()才销毁
当 co_return 没有返回值时,编译器会调用 `void promise_type::return_void()` 函数来处理返回逻辑,然后执行 final_suspend() 函数,根据其返回值决定协程是否销毁。
#include <iostream>
#include <coroutine>
struct CoroRAII
{
struct promise_type
{
CoroRAII get_return_object()
{
return CoroRAII{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend()
{
return {};
}
void return_void()
{
std::cout << "协程结束" << std::endl;
}
std::suspend_always final_suspend() noexcept
{
return {};
}
void unhandled_exception()
{
std::cout << "未处理的异常" << std::endl;
}
};
std::coroutine_handle<promise_type> handle;
CoroRAII(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
void resume()
{
if (handle && !handle.done())
{
handle.resume();
}
}
~CoroRAII()
{
if (handle)
{
handle.destroy();
}
}
};
CoroRAII my_coroutine()
{
std::cout << "协程开始" << std::endl;
co_return;
}
int main()
{
CoroRAII coro = my_coroutine();
coro.resume();
return 0;
}
当 co_return 有返回值时,它会将返回值传递到 函数中处理,然后执行 void promise_type::return_value(T value)final_suspend() 函数,根据其返回值决定协程是否销毁。注意:
co_return返回值需要通过promise_type的成员变量存储与获取,建议封装返回值获取接口,避免外部直接操作handle和promise对象。return_value和return_void互斥,即:协程函数要不有返回值、要不没有返回值
#include <iostream>
#include <coroutine>
struct CoroRAII
{
struct promise_type
{
int value;
CoroRAII get_return_object()
{
return CoroRAII{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend()
{
return {};
}
// 2. 存储结果
void return_value(int value)
{
this->value = value;
}
std::suspend_always final_suspend() noexcept
{
return {};
}
void unhandled_exception()
{
std::cout << "未处理的异常" << std::endl;
}
};
std::coroutine_handle<promise_type> handle;
CoroRAII(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
// 3. 返回结果
int value()
{
return handle.promise().value;
}
void resume()
{
if (handle && !handle.done())
{
handle.resume();
}
}
~CoroRAII()
{
if (handle)
{
handle.destroy();
}
}
};
CoroRAII my_coroutine()
{
std::cout << "协程开始" << std::endl;
// 1. 返回结果
co_return 100;
}
int main()
{
CoroRAII coro = my_coroutine();
coro.resume();
std::cout << coro.value() << std::endl;
return 0;
}
当协程函数在执行时,抛出异常,但是并没有处理异常时,编译器会自动调用:void promise_type::unhandled_exception() 来处理。
#include <iostream>
#include <coroutine>
struct CoroRAII
{
struct promise_type
{
int value;
CoroRAII get_return_object()
{
return CoroRAII{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend()
{
return {};
}
std::suspend_always final_suspend() noexcept
{
return {};
}
void return_void()
{
}
void unhandled_exception()
{
std::cout << "未处理的异常" << std::endl;
}
};
std::coroutine_handle<promise_type> handle;
CoroRAII(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
void resume()
{
if (handle && !handle.done())
{
handle.resume();
}
}
// 获得协程结果
int value()
{
if (handle)
{
return handle.promise().value;
}
return 0;
}
~CoroRAII()
{
if (handle)
{
handle.destroy();
}
}
};
CoroRAII my_coroutine()
{
std::cout << "协程开始" << std::endl;
throw std::runtime_error("抛出异常");
co_return;
}
int main()
{
CoroRAII coro = my_coroutine();
coro.resume();
return 0;
}
2.4 co_yield
与 co_return 直接终止协程执行不同,co_yield 并不会结束协程,而是先向调用方生成一个中间值,再将自身挂起。它无需依赖任何外部事件,完全由协程主动触发挂起,后续需等待调用方显式恢复才能继续执行。
具体的执行流程,当协程执行到 co_yield value; 时:会调用 auto promise_type::yield_value(T value) 函数,其返回值决定协程在 co_yield 后是否立即挂起:
std::suspend_always:协程挂起,调用者可以从外部通过resume()恢复执行std::suspend_never:协程不挂起,继续执行
#include <iostream>
#include <coroutine>
struct CoroManager
{
struct promise_type
{
int value;
CoroManager get_return_object()
{
return CoroManager{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend() { return {}; }
void return_void() {}
// std::suspend_always: 挂起;
// std::suspend_never:不挂起
std::suspend_always yield_value(int value)
{
this->value = value;
return {};
}
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle;
CoroManager(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
~CoroManager()
{
if (handle)
{
handle.destroy();
}
}
};
CoroManager my_coroutine()
{
co_yield 10;
co_yield 20;
co_yield 30;
co_return;
}
int main()
{
CoroManager coro = my_coroutine();
coro.handle.resume();
std::cout << "第一次返回的值:" << coro.handle.promise().value << std::endl;
coro.handle.resume();
std::cout << "第二次返回的值:" << coro.handle.promise().value << std::endl;
coro.handle.resume();
std::cout << "第三次返回的值:" << coro.handle.promise().value << std::endl;
return 0;
}
2.5 co_await
co_yield 挂起的逻辑很简单,就是返回结果后挂起。co_await 需要根据条件进行挂起,挂起的时候,可能还需要执行某些操作。恢复的时候,也会执行某些逻辑。这也决定了 co_await 后面不能是简单的表达式,而是一个可等待对象(实现 awaitable 协议的对象)。
struct Awaiter
{
// 是否继续执行
// true 表示继续执行,然后执行 await_resume() 返回结果
// false 表示挂起,然后执行 await_suspend() 进行挂起前的操作
bool await_ready();
// 返回 void:执行后挂起
// 返回 bool:true 表示执行完后挂起,false 表示执行完后不挂起,继续执行 await_resume 函数
// 返回 handle:表示执行完后,继续恢复执行 handle 关联的协程
void/bool/handle await_suspend(std::coroutine_handle<> handle);
// 协程恢复后执行,返回 co_await 的结果
T await_resume();
};
#include <iostream>
#include <coroutine>
struct MyAwaiter
{
int value;
// 1. 是否直接执行
bool await_ready() const noexcept
{
bool flag = true;
std::cout << "是否执行:" << std::boolalpha << flag << std::endl;
// true 不挂起:直接执行 await_resume 函数
// false 挂起:直接执行 await_suspend 函数
return flag;
}
// 2. 挂起时调用
void await_suspend(std::coroutine_handle<> h) noexcept
{
std::cout << "协程挂起" << std::endl;
}
// 3. 恢复时调用
int await_resume() noexcept
{
std::cout << "协程执行" << std::endl;
return 100;
}
};
struct CoroManager
{
struct promise_type
{
int value;
CoroManager get_return_object()
{
return CoroManager{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend()
{
return {};
}
void return_void()
{
std::cout << "协程结束" << std::endl;
}
std::suspend_always final_suspend() noexcept
{
return {};
}
void unhandled_exception()
{
std::terminate();
}
};
std::coroutine_handle<promise_type> handle;
CoroManager(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
~CoroManager()
{
if (handle)
{
handle.destroy();
std::cout << "协程销毁" << std::endl;
}
}
};
CoroManager coro_function()
{
int ret = co_await MyAwaiter();
std::cout << "协程结果: " << ret << std::endl;
co_return;
}
int main()
{
CoroManager coro = coro_function();
coro.handle.resume();
return 0;
}
在 promise_type 中定义 await_transform,可以拦截 co_await 后的表达式,并将其转换为一个满足 awaitable 协议的对象,无论原始表达式本身是否可等待。
#include <iostream>
#include <coroutine>
struct MyAwaiter
{
int value;
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<> handle) {}
int await_resume() { return value; }
};
struct CoroManager
{
struct promise_type
{
int value;
CoroManager get_return_object() { return CoroManager{ std::coroutine_handle<promise_type>::from_promise(*this) };}
std::suspend_always initial_suspend() { return {}; }
void return_void() {}
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
auto await_transform(int val)
{
return MyAwaiter{ val };
}
};
std::coroutine_handle<promise_type> handle;
CoroManager(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
void resume() { handle.resume(); }
~CoroManager(){ handle.destroy(); }
};
CoroManager my_croutine()
{
int ret = co_await 100;
std::cout << "ret = " << ret << std::endl;
}
int main()
{
CoroManager coro = my_croutine();
coro.resume();
coro.resume();
return 0;
}
3. 协程帧
协程帧是编译器为协程生成的一块独立存储区域,用于保存协程跨 co_await/co_yield 边界时必须保留的全部状态。它承载的内容包括局部变量、promise 对象、当前执行位置以及挂起点信息,是协程实现挂起与恢复执行的核心数据结构。
在 C++20 协程中,当一个协程被创建时,编译器默认会通过 new 为其分配协程帧(coroutine frame)所需的内存;当协程执行完毕并销毁时,则通过 delete 释放该内存。
这种默认行为对于大多数普通应用场景是足够安全且高效的。然而,在某些高性能或高并发场景下(例如每秒需要创建成千上万个短生命周期协程的服务器程序),频繁调用 new/delete 会带来显著的性能开销:不仅增加了内存分配/回收的 CPU 时间,还可能引发内存碎片,进而成为系统瓶颈。
为了解决这个问题,C++ 允许开发者通过自定义协程的内存分配策略(例如重载 operator new 和 operator delete)来接管协程帧的内存管理。这样可以大幅降低分配成本、提升内存访问局部性,并使性能表现更加可预测。
但需要注意的是,自定义内存管理会增加代码复杂度和维护成本。因此,除非你的程序确实面临协程数量庞大、生命周期极短、且内存分配已成为性能瓶颈的情况,否则通常没有必要放弃默认的内存管理机制。
#include <iostream>
#include <coroutine>
struct CoroManager
{
struct promise_type
{
static void* operator new(size_t size)
{
void* ptr = ::operator new(size);
std::cout << "分配协程帧内存: " << ptr << " " << size << std::endl;
return ptr;
}
static void operator delete(void* ptr)
{
std::cout << "释放协程帧内存: " << ptr << std::endl;
::operator delete(ptr);
}
CoroManager get_return_object() { return CoroManager{ std::coroutine_handle<promise_type>::from_promise(*this) };}
std::suspend_always initial_suspend() { return {}; }
void return_void(){ std::cout << "协程结束" << std::endl; }
std::suspend_always final_suspend() noexcept { return {};}
void unhandled_exception(){}
};
std::coroutine_handle<promise_type> handle;
CoroManager(std::coroutine_handle<promise_type> handle) : handle(handle){}
void resume(){ handle.resume(); }
~CoroManager(){ handle.destroy();}
};
CoroManager my_coroutine()
{
std::cout << "协程开始" << std::endl;
co_return;
}
int main()
{
CoroManager coro = my_coroutine();
coro.resume();
return 0;
}
4. 应用案例
4.1 惰性计算
只在结果被真正需要时才执行计算,而不是在定义时立刻执行。
#include <iostream>
int fibonacci()
{
static int prev1 = 0;
static int prev2 = 1;
int current = prev1;
int next = prev1 + prev2;
prev1 = prev2;
prev2 = next;
return current;
}
void demo()
{
for (int i = 0; i < 5; ++i)
{
int val = fibonacci();
std::cout << val << std::endl;
}
}
int main()
{
demo();
return 0;
}
#include <coroutine>
#include <iostream>
struct Generator
{
struct promise_type
{
int value;
Generator get_return_object()
{
return Generator{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend()
{
return {};
}
std::suspend_never final_suspend() noexcept
{
return {};
}
std::suspend_always yield_value(int value)
{
this->value = value;
return {};
}
void return_void()
{
std::cout << "协程结束" << std::endl;
}
void unhandled_exception()
{
std::terminate();
}
};
std::coroutine_handle<promise_type> handle;
explicit Generator(std::coroutine_handle<promise_type> handle)
{
this->handle = handle;
}
// 获取下一个值(返回是否有值)
void next()
{
handle.resume();
}
// 获取当前值
int value() const
{
return handle.promise().value;
}
~Generator()
{
if (handle)
{
handle.destroy();
}
}
};
Generator fibonacci()
{
int prev1 = 0;
int prev2 = 1;
while (true)
{
int current = prev1;
int next = prev1 + prev2;
prev1 = prev2;
prev2 = next;
co_yield current;
}
}
int main()
{
// 创建协程
auto gen = fibonacci();
for (int i = 0; i < 10; ++i)
{
// 生成下一个数
gen.next();
// 获得数
std::cout << gen.value() << std::endl;
}
return 0;
}
4.2 异步编程
异步编程是一种以避免发起任务的执行线程被阻塞为目标的编程思想。 它通过将等待操作从当前执行路径中剥离,使线程在等待期间仍能继续处理其他任务,从而提升线程的整体利用效率。 在工程实现上,异步编程通常会结合多线程、线程池、多路 I/O 复用、定时器等机制来承载和管理这些等待。
#include <iostream>
#include <chrono>
#include <thread>
void task1()
{
std::cout << "task1 执行开始" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "task1 执行结束" << std::endl;
}
void task2()
{
std::cout << "task2 执行开始" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "task2 执行结束" << std::endl;
}
int main()
{
auto start = std::chrono::steady_clock::now();
task1();
task2();
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "程序耗时:" << duration.count() << "毫秒" << std::endl;
return 0;
}

- 初始化:将协程任务注册到事件循环中
- 开启事件循环:事件循环调度任务1,当发生阻塞时候,任务1挂起。执行权交给事件循环,事件循环调度任务2。任务1在挂起期间如果完成阻塞,通知事件循环,事件循环在合适时机再恢复任务1的执行。
#include <iostream>
#include <coroutine>
#include <chrono>
#include <thread>
#include <queue>
#include <mutex>
class EventLoop
{
public:
void add_task(std::coroutine_handle<> handle)
{
{
std::lock_guard<std::mutex> lock(my_mutex);
my_queue.push(handle);
}
++number;
}
void post(std::coroutine_handle<> handle)
{
{
std::lock_guard<std::mutex> lock(my_mutex);
my_queue.push(handle);
}
my_cv.notify_one();
}
void run()
{
while (true)
{
std::coroutine_handle<> my_handle;
{
std::unique_lock<std::mutex> lock(my_mutex);
// 如果队列为非空,结束阻塞
// 如果任务数为零,结束阻塞
my_cv.wait(lock, [&] { return !my_queue.empty() || 0 == number; });
if (my_queue.empty() && 0 == number)
{
std::cout << "任务执行完毕" << std::endl;
break;
}
my_handle = my_queue.front();
my_queue.pop();
}
if (!my_handle)
{
continue;
}
my_handle.resume();
if (my_handle.done())
{
--number;
my_handle.destroy();
}
}
}
private:
std::queue<std::coroutine_handle<>> my_queue;
std::mutex my_mutex;
std::condition_variable my_cv;
size_t number{ 0 };
};
EventLoop eloop;
struct Task
{
struct promise_type
{
Task get_return_object()
{
return Task{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend()
{
return {};
}
std::suspend_always final_suspend() noexcept
{
return {};
}
void return_void() {}
void unhandled_exception() { std::exit(1); }
};
operator std::coroutine_handle<>()
{
return handle;
}
std::coroutine_handle<promise_type> handle;
Task(std::coroutine_handle<promise_type> h) : handle(h) {}
void resume() { handle.resume(); }
};
struct AsyncSleep
{
int time;
// 挂起协程
bool await_ready()
{
return false;
}
void await_suspend(std::coroutine_handle<> handle)
{
// 模拟异步:另起线程等待后恢复协程
std::thread([handle, duration = time] {
std::this_thread::sleep_for(std::chrono::seconds(duration));
eloop.post(handle);
}).detach();
}
void await_resume() {}
};
Task task1()
{
std::cout << "task1 执行开始" << std::endl;
co_await AsyncSleep{ 5 };
std::cout << "task1 执行结束" << std::endl;
}
Task task2()
{
std::cout << "task2 执行开始" << std::endl;
co_await AsyncSleep{ 3 };
std::cout << "task2 执行结束" << std::endl;
}
int main()
{
auto start = std::chrono::steady_clock::now();
auto t1 = task1();
auto t2 = task2();
eloop.add_task(t1);
eloop.add_task(t2);
eloop.run();
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "程序耗时:" << duration.count() << "毫秒" << std::endl;
return 0;
}


冀公网安备13050302001966号
异步编程案例:事件循环里EventLoop 里的 post 函数为什么还要 my_queue.push(handle);?在main()函数不是已经add_task了吗?
1. 事件循环中会从队列中把任务取出来执行,队列中就不存在了。2. 取出来的任务执行过程中挂起,事件循环转而执行队列中其他任务。 3. 挂起的任务等待结束后,再重新添加到队列中,这样事件循环就可以重新调度了。