这对 "With a stackless coroutine, only the top-level routine may be suspended." 意味着什么
What does it mean for "With a stackless coroutine, only the top-level routine may be suspended."
我从 中找到了那句话。起初我很惊讶,因为我相信这使得无堆栈协程几乎毫无用处(而且 C++ 协程 TS 是无堆栈的)。所以我写了一个demo(In visual studio using C++ coroutine TS):
#include<experimental/coroutine>
#include<iostream>
#include<thread>
#include<mutex>
#include<future>
#include<chrono>
using namespace std;
using namespace std::chrono;
using namespace std::experimental;
class AsyncQueue {
public:
class Awaitable {
friend AsyncQueue;
AsyncQueue& mQueue;
coroutine_handle<> mCoroutineHandle;
Awaitable* mNext = nullptr;
public:
Awaitable(AsyncQueue& queue):mQueue(queue){}
bool await_ready() const noexcept {
return false;
}
bool await_suspend(coroutine_handle<> coroutineHandle) noexcept
{
mCoroutineHandle = coroutineHandle;
mQueue.enqueue(this);
return true;
}
void await_resume() noexcept {}
};
private:
mutex mMutex;
Awaitable* mHead = nullptr;
Awaitable* mTail = nullptr;
void enqueue(Awaitable* awaitable){
lock_guard<mutex> g{ mMutex };
if (mTail) {
mTail->mNext = awaitable;
mTail = awaitable;
}
else {
mTail = awaitable;
mHead = mTail;
}
}
Awaitable* dequeue() {
lock_guard<mutex> g{ mMutex };
Awaitable* result = mHead;
mHead = nullptr;
mTail = nullptr;
return result;
}
public:
Awaitable operator co_await() noexcept {
return Awaitable{ *this };
}
bool poll() {
Awaitable* awaitables = dequeue();
if (!awaitables) {
return false;
}
else {
while (awaitables) {
awaitables->mCoroutineHandle.resume();
awaitables = awaitables->mNext;
}
return true;
}
}
};
AsyncQueue toBackgroundThread;
AsyncQueue toMainThread;
std::future<void> secondLevel(int id)
{
co_await toBackgroundThread;
cout << id << " run on " << this_thread::get_id() << endl;
co_await toMainThread;
cout << id << " run on " << this_thread::get_id() << endl;
}
std::future<void> topLevel() {
co_await secondLevel(1);
co_await secondLevel(2);
}
void listen(AsyncQueue& queue) {
while (true) {
if (!queue.poll()) {
this_thread::sleep_for(100ms);
}
}
}
int main() {
thread([]() {
listen(toBackgroundThread);
}).detach();
topLevel();
listen(toMainThread);
}
coroutine topLevel
调用两个 secondLevel
(我认为它们是可挂起的非顶级例程),并且工作正常。
上面的代码打印:
1 run on 16648
1 run on 3448
2 run on 16648
2 run on 3448
根据该回答,This prohibits providing suspend/resume operations in routines within a general-purpose library.
我认为这里没有任何禁令。
在co_await
的每次调用中,只有顶层协程被挂起。要挂起较低级别,该级别 必须显式挂起自己。那时,它现在是当前的 "top level"。所以在任何情况下,只有当前的顶层被暂停。
将其与纯假设的堆栈协程库进行比较:
//This function will always print the same thread ID.
void secondLevel(int id)
{
while(!toBackgroundThread.poll())
suspend_coroutine();
cout << id << " run on " << this_thread::get_id() << endl;
while(!toBackgroundThread.poll())
suspend_coroutine();
cout << id << " run on " << this_thread::get_id() << endl;
}
void topLevel() {
secondLevel(1);
secondLevel(2);
}
void listen(AsyncQueue& queue) {
while (true) {
if (!queue.poll()) {
this_thread::sleep_for(100ms);
}
}
}
int main() {
thread([]() {
listen(toBackgroundThread);
}).detach();
auto coro = create_coroutine(topLevel);
coro.switch_to();
toMainThread.ready(); //Notes that the main thread is waiting
while (true) {
if (!toMainThread.poll()) {
coro.switch_to();
}
}
};
topLevel
没有任何明确的暂停机制。然而,只要它调用的任何函数暂停执行,它的执行就会暂停。由提供给 create_coroutine
的函数及其调用的所有内容定义的整个调用堆栈暂停。这就是堆栈协程的工作方式。
这就是无堆栈协程的对比。在无堆栈版本中,每个需要暂停的函数都必须专门编码才能这样做。因此不再是 "general purpose";它现在专门用于暂停场景。
我从
#include<experimental/coroutine>
#include<iostream>
#include<thread>
#include<mutex>
#include<future>
#include<chrono>
using namespace std;
using namespace std::chrono;
using namespace std::experimental;
class AsyncQueue {
public:
class Awaitable {
friend AsyncQueue;
AsyncQueue& mQueue;
coroutine_handle<> mCoroutineHandle;
Awaitable* mNext = nullptr;
public:
Awaitable(AsyncQueue& queue):mQueue(queue){}
bool await_ready() const noexcept {
return false;
}
bool await_suspend(coroutine_handle<> coroutineHandle) noexcept
{
mCoroutineHandle = coroutineHandle;
mQueue.enqueue(this);
return true;
}
void await_resume() noexcept {}
};
private:
mutex mMutex;
Awaitable* mHead = nullptr;
Awaitable* mTail = nullptr;
void enqueue(Awaitable* awaitable){
lock_guard<mutex> g{ mMutex };
if (mTail) {
mTail->mNext = awaitable;
mTail = awaitable;
}
else {
mTail = awaitable;
mHead = mTail;
}
}
Awaitable* dequeue() {
lock_guard<mutex> g{ mMutex };
Awaitable* result = mHead;
mHead = nullptr;
mTail = nullptr;
return result;
}
public:
Awaitable operator co_await() noexcept {
return Awaitable{ *this };
}
bool poll() {
Awaitable* awaitables = dequeue();
if (!awaitables) {
return false;
}
else {
while (awaitables) {
awaitables->mCoroutineHandle.resume();
awaitables = awaitables->mNext;
}
return true;
}
}
};
AsyncQueue toBackgroundThread;
AsyncQueue toMainThread;
std::future<void> secondLevel(int id)
{
co_await toBackgroundThread;
cout << id << " run on " << this_thread::get_id() << endl;
co_await toMainThread;
cout << id << " run on " << this_thread::get_id() << endl;
}
std::future<void> topLevel() {
co_await secondLevel(1);
co_await secondLevel(2);
}
void listen(AsyncQueue& queue) {
while (true) {
if (!queue.poll()) {
this_thread::sleep_for(100ms);
}
}
}
int main() {
thread([]() {
listen(toBackgroundThread);
}).detach();
topLevel();
listen(toMainThread);
}
coroutine topLevel
调用两个 secondLevel
(我认为它们是可挂起的非顶级例程),并且工作正常。
上面的代码打印:
1 run on 16648
1 run on 3448
2 run on 16648
2 run on 3448
根据该回答,This prohibits providing suspend/resume operations in routines within a general-purpose library.
我认为这里没有任何禁令。
在co_await
的每次调用中,只有顶层协程被挂起。要挂起较低级别,该级别 必须显式挂起自己。那时,它现在是当前的 "top level"。所以在任何情况下,只有当前的顶层被暂停。
将其与纯假设的堆栈协程库进行比较:
//This function will always print the same thread ID.
void secondLevel(int id)
{
while(!toBackgroundThread.poll())
suspend_coroutine();
cout << id << " run on " << this_thread::get_id() << endl;
while(!toBackgroundThread.poll())
suspend_coroutine();
cout << id << " run on " << this_thread::get_id() << endl;
}
void topLevel() {
secondLevel(1);
secondLevel(2);
}
void listen(AsyncQueue& queue) {
while (true) {
if (!queue.poll()) {
this_thread::sleep_for(100ms);
}
}
}
int main() {
thread([]() {
listen(toBackgroundThread);
}).detach();
auto coro = create_coroutine(topLevel);
coro.switch_to();
toMainThread.ready(); //Notes that the main thread is waiting
while (true) {
if (!toMainThread.poll()) {
coro.switch_to();
}
}
};
topLevel
没有任何明确的暂停机制。然而,只要它调用的任何函数暂停执行,它的执行就会暂停。由提供给 create_coroutine
的函数及其调用的所有内容定义的整个调用堆栈暂停。这就是堆栈协程的工作方式。
这就是无堆栈协程的对比。在无堆栈版本中,每个需要暂停的函数都必须专门编码才能这样做。因此不再是 "general purpose";它现在专门用于暂停场景。