最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++20 特性 協(xié)程 Coroutines(1)

 更新時(shí)間:2021年10月08日 10:46:55   作者:孫孟越  
這篇文章主要給大家分享得是C++20 得特性 協(xié)程 Coroutines,下面文章內(nèi)容我們將來具體介紹什么是協(xié)程,協(xié)程得好處等知識(shí)點(diǎn),需要的朋友可以參考一下

我們先來介紹一下什么是協(xié)程.

一、協(xié)程簡(jiǎn)單介紹

協(xié)程和普通的函數(shù) 其實(shí)差不多. 不過這個(gè) "函數(shù)" 能夠暫停自己, 也能夠被別人恢復(fù).

普通的函數(shù)調(diào)用, 函數(shù)運(yùn)行完返回一個(gè)值, 結(jié)束.

協(xié)程可以運(yùn)行到一半, 返回一個(gè)值, 并且保留上下文. 下次恢復(fù)的時(shí)候還可以接著運(yùn)行, 上下文 (比如局部變量) 都還在.

這就是最大的區(qū)別.

二、協(xié)程的好處

考慮多任務(wù)協(xié)作的場(chǎng)景. 如果是線程的并發(fā), 那么大家需要搶 CPU 用, 還需要條件變量/信號(hào)量或者上鎖等技術(shù), 來確保正確的線程正在工作.

如果在協(xié)程中, 大家就可以主動(dòng)暫停自己, 多個(gè)任務(wù)互相協(xié)作. 這樣可能就比大家一起搶 CPU 更高效一點(diǎn), 因?yàn)槟隳軌蚩刂颇膫€(gè)協(xié)程用上 CPU.

一個(gè)例子:

生產(chǎn)者/消費(fèi)者模型: 生產(chǎn)者生產(chǎn)完畢后, 暫停自己, 把控制流還給消費(fèi)者. 消費(fèi)者消費(fèi)完畢后, resume 生產(chǎn)者, 生產(chǎn)者繼續(xù)生產(chǎn). 這樣循環(huán)往復(fù).

異步調(diào)用: 比如你要請(qǐng)求網(wǎng)絡(luò)上的一個(gè)資源.

  • 發(fā)請(qǐng)求給協(xié)程
  • 協(xié)程收到請(qǐng)求以后, 發(fā)出請(qǐng)求. 協(xié)程暫停自己, 把控制權(quán)還回去.
  • 你繼續(xù)做些別的事情. 比如發(fā)出下一個(gè)請(qǐng)求. 或者做一些計(jì)算.
  • 恢復(fù)這個(gè)協(xié)程, 拿到資源 (可能還要再等一等)

理想狀態(tài)下, 4 可以直接用上資源, 這樣就完全不浪費(fèi)時(shí)間.

如果是同步的話:

  • 發(fā)請(qǐng)求給函數(shù).
  • 函數(shù)收到請(qǐng)求以后, 等資源.
  • 等了很久, 資源到了, 把控制權(quán)還回去.

明顯需要多等待一會(huì)兒. 如果需要發(fā)送上百個(gè)請(qǐng)求, 那顯然是第一種異步調(diào)用快一點(diǎn). (等待的過程中可以發(fā)送新的請(qǐng)求)

如果沒有協(xié)程的話, 解決方案之一是使用多線程. 像這樣:

  • 發(fā)請(qǐng)求給函數(shù).
  • 函數(shù)在另外的線程等, 不阻塞你的線程.
  • 你繼續(xù)做些別的事情. 比如發(fā)出下一個(gè)請(qǐng)求. 或者做一些計(jì)算.
  • 等到終于等到了, 他再想一些辦法通知你.

然后通知的辦法就有 promise 和回調(diào)這些辦法.

三、協(xié)程得用法

我們照著 C++20 標(biāo)準(zhǔn)來看看怎么用協(xié)程. 用 g++, 版本 10.2 進(jìn)行測(cè)試.

目前 C++20 標(biāo)準(zhǔn)只加入了協(xié)程的基本功能, 還沒有直接能上手用的類. GCC 說會(huì)盡量與 clang MSVC 保持協(xié)程的 ABI 兼容, 同時(shí)和 libc++ 等保持庫的兼容. 所以本文可能也適用于它們.

協(xié)程和主程序之間通過 promise 進(jìn)行通信. promise 可以理解成一個(gè)管道, 協(xié)程和其調(diào)用方都能看得到.

以前的 std::async std::future 也是基于一種特殊的 promise 進(jìn)行通信的, 就是 std::promise. 如果要使用協(xié)程, 則需要自己實(shí)現(xiàn)一個(gè)全新的 promise 類, 原理上是類似的.

四、協(xié)程三個(gè)關(guān)鍵字

這次引入了三個(gè)新的關(guān)鍵字 co_await, co_yield, co_return . 從效果上看: co_await 是用來暫停和恢復(fù)協(xié)程的, 并且真正用來求值.

co_yield 是用來暫停協(xié)程并且往綁定的 promise 里面 yield 一個(gè)值.

co_return 是往綁定的 promise 里面放入一個(gè)值.

這里我們先談?wù)?code> co_yield 和 co_return. 談完這倆再談?wù)?code> co_await 就比較簡(jiǎn)單.

五、協(xié)程工作原理

所以最重要的兩個(gè)問題就是

  • 協(xié)程如何實(shí)現(xiàn)信息傳遞 (使用自己實(shí)現(xiàn)的 promise)
  • 如何恢復(fù)一個(gè)已經(jīng)暫停了的協(xié)程 (使用 std::coroutine_handle)

上面說了, 一個(gè)協(xié)程會(huì)有一個(gè)與之相伴的 promise , 用作信息傳遞. 一個(gè)協(xié)程, 效果等同于

{
promise-type promise(promise-constructor-arguments); 
try {
    co_await promise.initial_suspend(); // 創(chuàng)建之后 第一次暫停
    function-body // 函數(shù)體
} catch ( ... ) {
    if (!initial-await-resume-called)
    throw; 
    promise.unhandled_exception(); 
}

final-suspend:
co_await promise.final_suspend(); // 最后一次暫停
}

細(xì)節(jié), 包括 promise 初始化的參數(shù), 異常的處理等等, 我們留到之后的文章再處理. 所以我們簡(jiǎn)化成

{
promise-type promise; 

co_await promise.initial_suspend(); 

function-body // 函數(shù)體

final-suspend:
co_await promise.final_suspend(); 
}


對(duì)于暫停, co_await 那個(gè)地方就可以暫停并且交出控制權(quán). 下篇文章我們會(huì)詳細(xì)介紹 co_await.

對(duì)于喚醒, 則需要拿到一個(gè) std::coroutine_handle, 對(duì)它調(diào)用 resume() .

1、co_yield

co_yield 123 做的事情實(shí)際上相當(dāng)于調(diào)用了 co_await promise.yield_value(123) . 這個(gè) promise 里面存放了 123 以后, 會(huì)告訴 co_await 自己要暫停. 于是 co_await 就在這里停下來, 把控制流還回去.

來看一個(gè)標(biāo)準(zhǔn)中的實(shí)現(xiàn)范例.

#include <iostream>
#include <coroutine>

struct generator
{
    struct promise_type;
    using handle = std::coroutine_handle<promise_type>;
    struct promise_type
    {
        int current_value;
        static auto get_return_object_on_allocation_failure() { return generator{nullptr}; }
        auto get_return_object() { return generator{handle::from_promise(*this)}; }
        auto initial_suspend() { return std::suspend_always{}; }
        auto final_suspend() { return std::suspend_always{}; }
        void unhandled_exception() { std::terminate(); }
        void return_void() {}
        auto yield_value(int value)
        {
            current_value = value;
            return std::suspend_always{}; // 這是一個(gè) awaiter 結(jié)構(gòu), 見第二篇文章
        }
    };
    bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
    int current_value() { return coro.promise().current_value; }
    generator(generator const &) = delete;
    generator(generator &&rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
    ~generator() {  if (coro) coro.destroy();   }

private:
    generator(handle h) : coro(h) {}
    handle coro;
};

generator f()
{
    co_yield 1;
    co_yield 2;
}

int main()
{
    auto g = f(); // 停在 initial_suspend 那里
    while (g.move_next()) // 每次調(diào)用就停在下一個(gè) co_await 那里
        std::cout << g.current_value() << std::endl;
}

generator 是一個(gè)包裝類, 持有一個(gè) std::coroutine_handle. 同時(shí)它規(guī)定了 coroutine_handle 本協(xié)程的 promise 是什么樣的. (通過 generator::promise_type告知)

coroutine_handle是協(xié)程的流程管理者, 由它來管理這個(gè) promise. 而 generator 則是 coroutine_handle 的管理者.

f() 是一個(gè)協(xié)程. 可以展開成這樣的偽代碼

{
generator g(handle coro); // 建立句柄和包裝類

co_await promise.initial_suspend(); // 創(chuàng)建之后停在這里, 等待被恢復(fù)

co_await promise.yield_value(1); // 第一次恢復(fù)后就會(huì)停在這里
co_await promise.yield_value(2); // 第二次恢復(fù)后就會(huì)停在這里

final-suspend:
co_await promise.final_suspend(); // 第三次恢復(fù)后就會(huì)停在這里
}

按照這里的寫法, 每一次 promise.yield_value() 之后都會(huì)返回一個(gè)結(jié)構(gòu)體給 co_await, 告訴 co_await 自己在這里暫停.

然后在主函數(shù)處調(diào)用 g.move_next() , 進(jìn)而恢復(fù)了協(xié)程之后, 協(xié)程就會(huì)從剛剛暫停的 co_await 那一行恢復(fù)運(yùn)行.

對(duì)了, 過了最后的 final_suspend() 以后, 這個(gè)協(xié)程就會(huì)析構(gòu)掉. 再次恢復(fù)協(xié)程就會(huì)導(dǎo)致 segmentation fault.

g++10 已經(jīng)提供了協(xié)程的支持, 只需要加上 -std=c++20 -fcoroutines -fno-exceptions 即可. 上面這段代碼可以在這里編譯:

2、co_return

co_return 相當(dāng)于調(diào)用了 promise.return_value() 或者 promise.return_void() 然后跳到 final-suspend 標(biāo)簽?zāi)抢? 也就是說這個(gè)這個(gè)協(xié)程結(jié)束了, 再也無法被恢復(fù)了.

而對(duì)比 co_yield 調(diào)用的是 co_await promise.yield_value(). 他們的區(qū)別就是 co_yeild 完了協(xié)程繼續(xù)等著下一次被恢復(fù) , co_return co_return完了協(xié)程就結(jié)束了. (為了讓協(xié)程也能像普通函數(shù)一樣返回)

我們來看一段代碼.

#include <iostream>
#include <future>
#include <coroutine>

using namespace std;

struct lazy
{
    struct promise_type;
    using handle = std::coroutine_handle<promise_type>;
    struct promise_type
    {
        int _return_value;
        static auto get_return_object_on_allocation_failure() { return lazy{nullptr}; }
        auto get_return_object() { return lazy{handle::from_promise(*this)}; }
        auto initial_suspend() { return std::suspend_always{}; }
        auto final_suspend() { return std::suspend_always{}; }
        void unhandled_exception() { std::terminate(); }
        void return_value(int value) { _return_value = value; }
    };
    bool calculate()
    {
        if (calculated)
            return true;
        if (!coro)
            return false;
        coro.resume();
        if (coro.done())
            calculated = true;
        return calculated;
    }
    int get() { return coro.promise()._return_value; }
    lazy(lazy const &) = delete;
    lazy(lazy &&rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
    ~lazy() {  if (coro) coro.destroy(); }

private:
    lazy(handle h) : coro(h) {}
    handle coro;
    bool calculated{false};
};


lazy f(int n = 0)
{
    co_return n + 1;
}

int main()
{
    auto g = f();
    g.calculate(); // 這時(shí)才從 initial_suspend 之中恢復(fù), 所以就叫 lazy 了
    cout << g.get();
}

由于這個(gè)協(xié)程只能被恢復(fù)一次, 所以我稍稍修改了一下 lazy 的實(shí)現(xiàn). 可以參考這里:

下一篇C++20 新特性 協(xié)程 Coroutines(2)

到此這篇關(guān)于C++20 特性 協(xié)程 Coroutines的文章就介紹到這了,更多相關(guān)C++20 協(xié)程 Coroutines內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++數(shù)組和指針的區(qū)別與聯(lián)系

    C++數(shù)組和指針的區(qū)別與聯(lián)系

    這篇文章主要介紹了C++數(shù)組和指針的區(qū)別與聯(lián)系,數(shù)組是具有固定大小和連續(xù)內(nèi)存空間的相同數(shù)據(jù)集合,指針是指存放內(nèi)存地址的變量,更多詳細(xì)資料請(qǐng)參考下面文章內(nèi)容
    2022-01-01
  • C語言實(shí)現(xiàn)簡(jiǎn)單五子棋游戲

    C語言實(shí)現(xiàn)簡(jiǎn)單五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)簡(jiǎn)單五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • C++JSON庫CJsonObject詳解(輕量簡(jiǎn)單好用)

    C++JSON庫CJsonObject詳解(輕量簡(jiǎn)單好用)

    CJsonObject是基于cJSON全新開發(fā)一個(gè)C++版的JSON庫,CJsonObject的最大優(yōu)勢(shì)是輕量簡(jiǎn)單好用,開發(fā)效率極高,對(duì)多層嵌套json的讀取和生成使用非常簡(jiǎn)單,喜歡的朋友一起看看吧
    2021-04-04
  • C語言中 type *(0)的具體使用

    C語言中 type *(0)的具體使用

    表達(dá)式?type * (0)?在 C/C++ 編程中是一個(gè)常見的技巧,通常用于內(nèi)核編程和一些系統(tǒng)編程場(chǎng)景中,本文主要介紹了C語言中 type *(0)的具體使用,感興趣的可以了解一下
    2024-08-08
  • C++詳解如何實(shí)現(xiàn)兩個(gè)線程交替打印

    C++詳解如何實(shí)現(xiàn)兩個(gè)線程交替打印

    這篇文章主要介紹了使用C++庫實(shí)現(xiàn)兩個(gè)線程交替打印,一個(gè)線程打印奇數(shù)、一個(gè)線程打印偶數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 區(qū)分c++中的聲明與定義

    區(qū)分c++中的聲明與定義

    這篇文章主要介紹了如何區(qū)分c++中的聲明與定義,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下
    2020-08-08
  • C++中靜態(tài)存儲(chǔ)區(qū)與棧以及堆的區(qū)別詳解

    C++中靜態(tài)存儲(chǔ)區(qū)與棧以及堆的區(qū)別詳解

    本篇文章是對(duì)C++中靜態(tài)存儲(chǔ)區(qū)與棧以及堆的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 簡(jiǎn)單掌握C++中的函數(shù)模板

    簡(jiǎn)單掌握C++中的函數(shù)模板

    這篇文章主要介紹了C++中的函數(shù)模板,包括函數(shù)模板的聲明和生成以及異常處理等基本知識(shí),需要的朋友可以參考下
    2016-04-04
  • C語言結(jié)構(gòu)體struct詳解

    C語言結(jié)構(gòu)體struct詳解

    C語言中,結(jié)構(gòu)體類型屬于一種構(gòu)造類型(其他的構(gòu)造類型還有:數(shù)組類型,聯(lián)合類型),下面這篇文章主要給大家介紹了關(guān)于C語言結(jié)構(gòu)體(struct)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • C++將txt文件內(nèi)容保存到數(shù)組的方法

    C++將txt文件內(nèi)容保存到數(shù)組的方法

    今天小編就為大家分享一篇C++將txt文件內(nèi)容保存到數(shù)組的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07

最新評(píng)論

文安县| 舟曲县| 武威市| 邯郸市| 松阳县| 双峰县| 辽阳市| 平罗县| 五峰| 平度市| 永兴县| 瓦房店市| 年辖:市辖区| 扎赉特旗| 海丰县| 勃利县| 图木舒克市| 融水| 阳信县| 盐池县| 泗洪县| 陕西省| 镇赉县| 原阳县| 得荣县| 拉萨市| 嫩江县| 杭锦后旗| 商洛市| 定边县| 昆明市| 马关县| 合阳县| 栖霞市| 平和县| 东宁县| 来宾市| 阿尔山市| 昭通市| 华蓥市| 永康市|