要像 C#
co_await
运算符一样使用 C++ await
运算符,我应该执行哪些最少操作?
cppreferense上有一篇关于协程的文章,其中有C#类
task<>
用作返回类型,但我在标准库中找不到它。当函数返回 void 时,这可以预见会导致编译错误。
task
只是您自己组装的管道的占位符。
来自 https://en.cppreference.com/w/cpp/language/coroutines
每个协程必须有一个满足许多要求的返回类型
当您在代码中使用
co_*
时,您需要 #include <coroutine>
。编译器会对你的函数做一些事情。示例:
void csp_await() {
bidirectional_channel<int> ch;
co_await ch.send(1);
}
上面的代码会导致以下错误:
“std::coroutine_traits”类没有成员“promise_type”
无论编译器在做什么,它都会寻找
promise_type
但找不到它。
为了解决这个问题,我们需要引入以下代码。
struct task_handle {};
struct task : std::coroutine_handle<task_handle> {
using promise_type = task_handle;
};
这给了我们一个不同的错误。
csp_await
函数的返回类型现已更改为使用我们的 task
类型。
“task::promise_type”没有成员“initial_suspend”
所以我们添加一些更多的东西
struct task_handle;
struct task : std::coroutine_handle<task_handle> {
using promise_type = task_handle;
};
struct task_handle {
task get_return_object() { return {task::from_promise(*this)}; }
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
接下来要做的就是让
send
协程的 bidirectional_channel
函数变得友好。它目前是一个返回类型为 void
的阻塞函数,不能按原样使用。
template <typename T> struct send_async_awaiter {
bidirectional_channel<int> &ch_;
T val_;
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> handle) {}
void await_resume() noexcept {}
};
template <typename T> send_async_awaiter<T> send_async(bidirectional_channel<T> &ch, const T &val) {
return send_async_awaiter{ch, val};
}
send_async_awaiter
是我们可等待的、协程友好的类型。 send_async
正是需要将 send
函数更改为可等待的。该代码可以编译,但没有执行任何操作。
我不想谈论 CSP,但我需要详细介绍一下通道发送函数中需要发生的事情。在这个例子中,我使用了一个无缓冲的通道,它应该挂起,直到有一个准备好的接收器。假设接收方尚未准备好开始,我们需要将发送操作停放在某个地方。为此,我们只需要考虑
await_ready
和 await_suspend
。 await_ready
返回 false,因此接下来发生的事情将是 await_suspend
。我们得到了一个协程的通用句柄,我们可以用它来停放它。
我一开始的
bidirectional_channel
的实现是基于线程的。它使用互斥体和条件变量来唤醒阻塞的线程,但我认为 bidirectional_channel_async
类型不应该以相同的方式实现。相反,我将考虑一种侵入式方法,将暂停的协程停放在通道内。我认为这是有道理的,因为这是我稍后需要能够找到停放的协程以在接收器准备就绪时恢复的地方。它还意味着我们可以编写一个完全单线程的 CSP 库,它使用协作式而不是抢占式多线程。我认为向这个异步 CSP 库添加并行性比采用其他方式更容易。
我没有详细介绍这里的所有细节,但这是一个开始。我很高兴它像这样解耦,因为你可以在此基础上构建任何你认为最好的东西。