Что это значит для «С сопрограммой без стеков, только подпрограмма верхнего уровня может быть приостановлена».

Я нашел это заявление от Вот. Сначала я был удивлен, потому что я считаю, что это делает сопрограммы без стеков практически бесполезными (а C + сопрограммы TS без стеков). Итак, я написал демо (в Visual Studio с использованием сопрограммы C ++ 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);
}

сопрограммная 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. Я не вижу здесь никаких запретов.

4

Решение

В каждом вызове co_await, только сопрограмма верхнего уровня приостановлена. Чтобы приостановить более низкий уровень, этот уровень должен приостановить себя явно. И на тот момент, это сейчас текущий «верхний уровень». Таким образом, в каждом случае только текущий верхний уровень будет приостановлен.

Сравните это с чисто гипотетической стопкой сопрограммной библиотеки:

//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 и все, что он вызывает, приостанавливается. Вот как работает сложная сопрограмма.

Это то, что противопоставляется, когда речь идет о сопрограммах без стеков. В версии без стека каждая функция, которая должна быть приостановлена, должна быть специально закодировано сделать это. И, таким образом, больше не является «общим назначением»; теперь это специально для сценариев приостановки.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]