代码之家  ›  专栏  ›  技术社区  ›  J.Doe

它的意思是“有了无堆叠协程,只有顶级的程序可以暂停。”

  •  4
  • J.Doe  · 技术社区  · 6 年前

    我是从 here . 起初,我感到惊讶,因为我相信这使得无堆栈协同程序几乎没有用(和C++ +协同程序TS是无栈)。所以我写了一个演示(在VisualStudio中使用C++ CONDOTIN 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. 我看这里没有禁止。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Nicol Bolas    6 年前

    每次调用 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 它所呼叫的一切都暂停了。这就是堆积如山的连体衣的工作原理。

    这就是当涉及到无堆叠协程时所要对比的。在无堆栈版本中,每个需要挂起的函数都必须 具体编码 这样做。因此不再是真正的“通用目的”;它现在特别适用于暂停场景。

    推荐文章