【发布时间】:2021-06-13 23:37:23
【问题描述】:
在 C# 中,支持嵌套等待,如下例所示。编译器会将代码的恢复拼接在一起(即方法 1 中的返回计数将在外层调用 int count=,然后调用方法 3 和 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中的协程的代码。您可能希望在final_suspend中恢复该协程。 -
@Raymond Chen,我确实尝试调用 handle.resume 但它导致了崩溃。
-
您的代码将
h_用于两件不同的事情——get() 假定它保存了正在返回的协程,但await_suspend存储了正在等待的协程。 -
@RaymondChen,我简化了代码以使关键问题更清晰。 Foo2 恢复后(按预期显示“在 Foo2\n 中恢复”),如何从那里恢复调用者框架的等待?我希望能够点击代码“在 Foo1 中恢复”。在Foo2的final_suspect,我只能获取当前Foo2的协程的协程句柄;我无法获得其父母的。所以 Foo1 的恢复代码(即“在 Foo1\n 中恢复”)不能被命中。 C++ 协程框架是否提供回退协程?
-
return_type的await_suspend方法需要将父协程句柄保存在promise中,这样promise在遇到final_suspend时可以恢复它。
标签: c++ c++20 c++-coroutine