C++20图片下载器工具项目实战
Awaitable 本身并不是一个具体的类或接口,而是通过特定的协议(protocol)与协程框架交互。换句话说,Awaitable 是提供 Awaiter 的类型,而 Awaiter 是包含。是用户面对的接口,通常是一个更高层次的类型,封装了异步操作的逻辑。如果 Awaitable 不是 Awaiter,编译器调用。3. Awaitable 和 Awaiter 的关系。3. Awaitable
C++20图片下载器工具项目实战
大纲:
-
1. Awaitable 是什么?
-
2. Awaiter 是什么?
-
3. Awaitable 和 Awaiter 的关系
-
4.图片下载工具实战
在 C++20 的协程(coroutines)框架中,Awaiter 和 Awaitable 是实现异步操作和协程暂停/恢复机制的核心概念。它们在协程的执行流程中扮演重要角色,尤其是在使用 co_await
关键字时。以下是对这两者的详细解析,并通过编写一个图片下载工具来深入理解。
注:懒人版,本节代码已更新至星球
1. Awaitable 是什么?
Awaitable 是一个广义的概念,指可以被 co_await
操作符暂停的对象。它通常是一个类型,定义了协程暂停和恢复的逻辑。Awaitable 本身并不是一个具体的类或接口,而是通过特定的协议(protocol)与协程框架交互。
Awaitable 的核心要求
一个类型要成为 Awaitable,必须满足以下条件(通过成员函数或自由函数实现):
-
await_ready()
:检查异步操作是否已经完成。如果返回true
,协程不会暂停,直接继续执行;如果返回false
,协程将暂停。 -
await_suspend()
:在协程暂停时调用,负责保存状态或注册回调。通常返回void
、bool
或std::coroutine_handle
,以控制协程的调度。 -
await_resume()
:在协程恢复时调用,返回co_await
表达式的结果。
这些函数可以通过以下方式定义:
-
作为 Awaitable 类型的成员函数。
-
作为自由函数(通过 ADL,Argument-Dependent Lookup)。
Awaitable 示例
以下是一个简单的 Awaitable 类型,用于模拟异步延迟:
#include <coroutine>
#include <chrono>
#include <thread>
#include <iostream>
struct Delay {
int milliseconds;
bool await_ready() const noexcept { returnfalse; } // 总是需要暂停
void await_suspend(std::coroutine_handle<> handle) const noexcept {
// 模拟异步延迟
std::thread([handle, ms = milliseconds]() {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
handle.resume(); // 恢复协程
}).detach();
}
void await_resume() const noexcept {
std::cout << "Resumed after delay\n";
}
};
这个 Delay
类型是一个 Awaitable,可以被 co_await
使用。
2. Awaiter 是什么?
Awaiter 是 Awaitable 的具体实现,或者说是 co_await
表达式求值后实际执行暂停/恢复逻辑的对象。换句话说,Awaitable 是提供 Awaiter 的类型,而 Awaiter 是包含 await_ready
、await_suspend
和 await_resume
方法的实体。
Awaiter 的来源
当你使用 co_await expr
:
-
编译器首先评估
expr
得到一个 Awaitable 对象。 -
如果 Awaitable 本身满足 Awaiter 协议(即有
await_ready
等方法),它直接作为 Awaiter。 -
否则,编译器会尝试调用 Awaitable 的
operator co_await()
(成员或自由函数)来获取一个 Awaiter。
Awaiter 示例
扩展上面的 Delay
示例,假设我们希望 Awaitable 和 Awaiter 分离:
struct DelayAwaiter {
int milliseconds;
bool await_ready() const noexcept { returnfalse; }
void await_suspend(std::coroutine_handle<> handle) const noexcept {
std::thread([handle, ms = milliseconds]() {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
handle.resume();
}).detach();
}
void await_resume() const noexcept {
std::cout << "Resumed after delay\n";
}
};
struct Delay {
int milliseconds;
DelayAwaiter operator co_await() const noexcept {
return DelayAwaiter{milliseconds};
}
};
在这里,Delay
是 Awaitable,它通过 operator co_await
返回一个 DelayAwaiter
(即 Awaiter)。
3. Awaitable 和 Awaiter 的关系
-
Awaitable 是用户面对的接口,通常是一个更高层次的类型,封装了异步操作的逻辑。
-
Awaiter 是底层实现,包含具体的暂停和恢复逻辑。
-
转换过程:
-
-
co_await expr
中的expr
求值为 Awaitable。 -
如果 Awaitable 不是 Awaiter,编译器调用
operator co_await()
获取 Awaiter。 -
Awaiter 的三个方法(
await_ready
,await_suspend
,await_resume
)控制协程的行为。
-
4.图片下载工具实战
-
前提条件
支持C++20的gcc/clang,安装了libcurl。
核心功能实现如下:
-
协程function功能
// 1.co_await
HttpRequest req{url, &response_data};
std::string result = co_await req; // 等待 HTTP 请求完成
// 2.保存文件
out_file.write(result.data(), result.size());
-
实现Awaitable:HTTP 请求对象
// Awaitable:HTTP 请求对象
struct HttpRequest {
std::string url;
std::string* response_data;
HttpRequest(const std::string& url, std::string* data) : url(url), response_data(data) {}
HttpRequestAwaiter operator co_await() const noexcept {
return HttpRequestAwaiter{url, response_data};
}
};
-
实现Awaiter
struct HttpRequestAwaiter {
bool await_ready() const noexcept { returnfalse; } // 总是异步执行
void await_suspend(std::coroutine_handle<> handle) noexcept {
// 在单独线程中执行 HTTP 请求
std::thread([this, handle]() {
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "CURL error: " << curl_easy_strerror(res) << "\n";
}
handle.resume(); // 恢复协程
}).detach();
}
std::string await_resume() const {
if (res != CURLE_OK) {
throwstd::runtime_error("HTTP request failed: " + std::string(curl_easy_strerror(res)));
}
return *response_data; // 返回响应数据
}
};
最终的效果:
学习更多干货,欢迎关注转发!
更多推荐
所有评论(0)