如何解决嵌套 co_await/coroutine 类似于 C# 嵌套等待样式
在 C# 中,支持嵌套等待,如下例所示。编译器会将代码的恢复拼接在一起(即方法 1 中的返回计数将调用外层中的 int count=,然后调用 method3 和 console.ReadKey())。但是我发现在 C++ 协程框架中很难做到这一点。
class Program
{
static void Main(string[] args)
{
callMethod();
Console.ReadKey();
}
public static async void callMethod()
{
Task<int> task = Method1();
Method2();
int count = await task;
Method3(count);
}
public static async Task<int> Method1()
{
int count = 0;
await Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(" Method 1");
count += 1;
}
});
return count;
}
public static void Method2()
{
for (int i = 0; i < 25; i++)
{
Console.WriteLine(" Method 2");
}
}
public static void Method3(int count)
{
Console.WriteLine("Total count is " + count);
}
}
在 C++ 协程框架中(以下是我在 C++ 中失败的尝试),如何在调用“co_await Foo2()”后恢复代码?似乎我们需要有问题地将协程句柄链接在一起,并在最深的 co_await 恢复后调用它。但是怎么做呢?下面是输出。
140717656352576 Promise created
Send back a return_type
Created a return_type object
140717656352576 Started the coroutine,don't stop now!
140717656352576 enter Foo1
140717656352576 Promise created
Send back a return_type
Created a return_type object
140717656352576 Started the coroutine,don't stop now!
140717656352576 enter Foo2
140717656352576 await_suspend
140717656352576 await_suspend in return_type
140717656352576 After Foo1
140717656348416 in Run
140717656348416 await_resume
140717656348416 resume in Foo2
140717656348416 Finished the coro --> **anyway I can call Foo1's handle to resume here?**
140717656348416 Promise died
return_type gone
下面是关于如何在评论中进行嵌套简历的关键问题的代码。
void run(std::coroutine_handle<> h)
{
std::cout<<std::this_thread::get_id()<<" "<<"in Run\n";
std::this_thread::sleep_for (std::chrono::seconds(5));
h.resume();
}
struct MyObj {
MyObj():v_(0){}
MyObj(int v):v_(v){}
int get() { return v_; }
int v_;
};
struct return_type {
return_type() {
std::cout << "Created a return_type object"<<std::endl;
}
~return_type() {
std::cout << "return_type gone" << std::endl;
}
struct promise_type {
promise_type() {
std::cout<<std::this_thread::get_id() <<" Promise created" << std::endl;
}
~promise_type() {
std::cout<<std::this_thread::get_id() << " Promise died" << std::endl;
}
auto get_return_object() {
std::cout << "Send back a return_type" <<std::endl;
return return_type();
}
auto initial_suspend() {
std::cout<<std::this_thread::get_id() <<" Started the coroutine,don't stop now!" << std::endl;
return std::suspend_never{};
}
auto final_suspend() {
std::cout<<std::this_thread::get_id() << " Finished the coro" << std::endl;
return std::suspend_never{};
}
void unhandled_exception() {
std::exit(1);
}
};
constexpr bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<promise_type> h) {
std::cout<<std::this_thread::get_id()<<" "<<"await_suspend in return_type\n";
}
void await_resume() const noexcept {
std::cout<<std::this_thread::get_id()<<" "<<"await_resume in resume_type\n";
}
};
struct Awaitable {
constexpr bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> h)
{
std::cout<<std::this_thread::get_id()<<" "<<"await_suspend\n";
std::thread t(run,h);
t.detach();
}
void await_resume() const noexcept {
std::cout<<std::this_thread::get_id()<<" "<<"await_resume\n";
}
};
return_type Foo2()
{
std::cout<<std::this_thread::get_id()<<" "<<"enter Foo2\n";
Awaitable a;
co_await a;
std::cout<<std::this_thread::get_id()<<" "<<"resume in Foo2\n";
**// This is where promise_type::final_suspend() is called
// Naturally,I'd want to call previous (Foo1)'s handle to resume
// but I have no way of doing so.**
}
return_type Foo1()
{
std::cout<<std::this_thread::get_id()<<" "<<"enter Foo1\n";
co_await Foo2();
std::cout<<std::this_thread::get_id()<<" resume in Foo1\n";
}
int main() {
auto r = Foo1();
std::cout<<std::this_thread::get_id()<<" After Foo1 \n";;
std::this_thread::sleep_for (std::chrono::seconds(10));
}
解决方法
await_suspend
的 return_type
需要将协程句柄交给 promise_type
,以便 promise_type
可以在 final_suspend
期间恢复 awaiter。查看标有 !!
的评论。
struct return_type {
std::coroutine_handle<>& waiting_; // !! To communicate with the promise_type
return_type(std::coroutine_handle<>& waiting) : waiting_(waiting) { // !! save it
std::cout << "Created a return_type object"<<std::endl;
}
~return_type() {
std::cout << "return_type gone" << std::endl;
}
struct promise_type {
promise_type() {
std::cout<<std::this_thread::get_id() <<" Promise created" << std::endl;
}
~promise_type() {
std::cout<<std::this_thread::get_id() << " Promise died" << std::endl;
}
auto get_return_object() {
std::cout << "Send back a return_type" <<std::endl;
return return_type(waiting_); // !! To communicate with the return_type
}
auto initial_suspend() {
std::cout<<std::this_thread::get_id() <<" Started the coroutine,don't stop now!" << std::endl;
return std::suspend_never{};
}
auto final_suspend() noexcept { // !! you forgot "noexcept"
std::cout<<std::this_thread::get_id() << " Finished the coro" << std::endl;
if (waiting_) waiting_.resume(); // !! resume anybody who is awaiting
return std::suspend_never{};
}
void unhandled_exception() {
std::exit(1);
}
void return_void() {} // !! you forgot this
std::coroutine_handle<> waiting_; // !! the awaiting coroutine
};
constexpr bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<promise_type> h) {
std::cout<<std::this_thread::get_id()<<" "<<"await_suspend in return_type\n";
waiting_ = h; // !! tell the promise_type who to resume when finished
}
void await_resume() const noexcept {
std::cout<<std::this_thread::get_id()<<" "<<"await_resume in resume_type\n";
}
};
return_type Foo2()
{
std::cout<<std::this_thread::get_id()<<" "<<"enter Foo2\n";
Awaitable a;
co_await a;
std::cout<<std::this_thread::get_id()<<" "<<"resume in Foo2\n";
co_return; // !! this ends Foo2() and resumes Foo1
}
这显示了如何将协程与其父级连接。但请注意,此代码仍有许多其他问题,但我没有尝试解决它们,因为它们不是问题的一部分。
,我通过将 promise 对象链接到 await_suspend 来使嵌套工作的一种方法。 结果:
140053703153472 enter Foo0
140053703153472 enter Foo1
140053703153472 enter Foo2
140053703153472 main thread wait for 10 seconds
140053703149312 async operation completed
140053703149312 resume in Foo2
140053703149312 resume in Foo1
140053703149312 resume in Foo0
代码:
void run(std::coroutine_handle<> h)
{
std::cout<<std::this_thread::get_id()<<" "<<"in Run\n";
std::this_thread::sleep_for (std::chrono::seconds(5));
h.resume();
}
struct return_type {
struct promise_type {
auto get_return_object() {
return return_type(std::coroutine_handle<promise_type>::from_promise(*this));
}
auto initial_suspend() {
return std::suspend_never{};
}
auto final_suspend() {
if (prev_ != nullptr) {
auto hh = std::coroutine_handle<promise_type>::from_promise(*prev_);
hh.resume();
}
return std::suspend_never{};
}
void unhandled_exception() {
std::exit(1);
}
void return_void() {}
promise_type* prev_ = nullptr;
};
return_type(bool async) : async_(async) {}
return_type(std::coroutine_handle<promise_type> h) : handle_{h} {
}
constexpr bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<promise_type> h) {
if (async_) {
std::thread t([&promise = h.promise()](){
std::this_thread::sleep_for (std::chrono::seconds(1));
std::cout<<std::this_thread::get_id()<<" "<<"async operation completed\n";
auto h = std::coroutine_handle<promise_type>::from_promise(promise);
h.resume();
});
t.detach();
} else {
handle_.promise().prev_ = &h.promise();
}
}
void await_resume() const noexcept {
}
std::coroutine_handle<promise_type> handle_;
bool async_ = false;
};
return_type Foo2()
{
std::cout<<std::this_thread::get_id()<<" "<<"enter Foo2\n";
return_type r(true);
co_await r;
std::cout<<std::this_thread::get_id()<<" "<<"resume in Foo2\n";
co_return;
}
return_type Foo1()
{
std::cout<<std::this_thread::get_id()<<" "<<"enter Foo1\n";
co_await Foo2();
std::cout<<std::this_thread::get_id()<<" resume in Foo1\n";
co_return;
}
return_type Foo0()
{
std::cout<<std::this_thread::get_id()<<" "<<"enter Foo0\n";
co_await Foo1();
std::cout<<std::this_thread::get_id()<<" resume in Foo0\n";
co_return;
}
int main() {
auto r = Foo0();
std::cout<<std::this_thread::get_id()<<" main thread wait for 10 seconds\n";;
std::this_thread::sleep_for (std::chrono::seconds(10));
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。