激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - C++20 特性 協程 Coroutines(1)

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

2022-01-19 13:03孫孟越 C/C++

這篇文章主要給大家分享得是C++20 得特性 協程 Coroutines,下面文章內容我們將來具體介紹什么是協程,協程得好處等知識點,需要的朋友可以參考一下

我們先來介紹一下什么是協程.

一、協程簡單介紹

協程和普通的函數 其實差不多. 不過這個 "函數" 能夠暫停自己, 也能夠被別人恢復.

普通的函數調用, 函數運行完返回一個值, 結束.

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

這就是最大的區別.

二、協程的好處

考慮多任務協作的場景. 如果是線程的并發, 那么大家需要搶 CPU 用, 還需要條件變量/信號量或者上鎖等技術, 來確保正確的線程正在工作.

如果在協程中, 大家就可以主動暫停自己, 多個任務互相協作. 這樣可能就比大家一起搶 CPU 更高效一點, 因為你能夠控制哪個協程用上 CPU.

一個例子:

生產者/消費者模型: 生產者生產完畢后, 暫停自己, 把控制流還給消費者. 消費者消費完畢后, resume 生產者, 生產者繼續生產. 這樣循環往復.

異步調用: 比如你要請求網絡上的一個資源.

  • 發請求給協程
  • 協程收到請求以后, 發出請求. 協程暫停自己, 把控制權還回去.
  • 你繼續做些別的事情. 比如發出下一個請求. 或者做一些計算.
  • 恢復這個協程, 拿到資源 (可能還要再等一等)

理想狀態下, 4 可以直接用上資源, 這樣就完全不浪費時間.

如果是同步的話:

  • 發請求給函數.
  • 函數收到請求以后, 等資源.
  • 等了很久, 資源到了, 把控制權還回去.

明顯需要多等待一會兒. 如果需要發送上百個請求, 那顯然是第一種異步調用快一點. (等待的過程中可以發送新的請求)

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

  • 發請求給函數.
  • 函數在另外的線程等, 不阻塞你的線程.
  • 你繼續做些別的事情. 比如發出下一個請求. 或者做一些計算.
  • 等到終于等到了, 他再想一些辦法通知你.

然后通知的辦法就有 promise 和回調這些辦法.

三、協程得用法

我們照著 C++20 標準來看看怎么用協程. 用 g++, 版本 10.2 進行測試.

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

協程和主程序之間通過 promise 進行通信. promise 可以理解成一個管道, 協程和其調用方都能看得到.

以前的 std::async std::future 也是基于一種特殊的 promise 進行通信的, 就是 std::promise. 如果要使用協程, 則需要自己實現一個全新的 promise 類, 原理上是類似的.

四、協程三個關鍵字

這次引入了三個新的關鍵字 co_await, co_yield, co_return . 從效果上看: co_await 是用來暫停和恢復協程的, 并且真正用來求值.

co_yield 是用來暫停協程并且往綁定的 promise 里面 yield 一個值.

co_return 是往綁定的 promise 里面放入一個值.

這里我們先談談 co_yield co_return. 談完這倆再談談 co_await 就比較簡單.

五、協程工作原理

所以最重要的兩個問題就是

  • 協程如何實現信息傳遞 (使用自己實現的 promise)
  • 如何恢復一個已經暫停了的協程 (使用 std::coroutine_handle)

上面說了, 一個協程會有一個與之相伴的 promise , 用作信息傳遞. 一個協程, 效果等同于

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
promise-type promise(promise-constructor-arguments);
try {
    co_await promise.initial_suspend(); // 創建之后 第一次暫停
    function-body // 函數體
} catch ( ... ) {
    if (!initial-await-resume-called)
    throw;
    promise.unhandled_exception();
}
 
final-suspend:
co_await promise.final_suspend(); // 最后一次暫停
}

細節, 包括 promise 初始化的參數, 異常的處理等等, 我們留到之后的文章再處理. 所以我們簡化成

?
1
2
3
4
5
6
7
8
9
10
{
promise-type promise;
 
co_await promise.initial_suspend();
 
function-body // 函數體
 
final-suspend:
co_await promise.final_suspend();
}

 

對于暫停, co_await 那個地方就可以暫停并且交出控制權. 下篇文章我們會詳細介紹 co_await.

對于喚醒, 則需要拿到一個 std::coroutine_handle, 對它調用 resume() .

1、co_yield

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

來看一個標準中的實現范例.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#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{}; // 這是一個 awaiter 結構, 見第二篇文章
        }
    };
    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()) // 每次調用就停在下一個 co_await 那里
        std::cout << g.current_value() << std::endl;
}

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

coroutine_handle是協程的流程管理者, 由它來管理這個 promise. 而 generator 則是 coroutine_handle 的管理者.

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

?
1
2
3
4
5
6
7
8
9
10
11
{
generator g(handle coro); // 建立句柄和包裝類
 
co_await promise.initial_suspend(); // 創建之后停在這里, 等待被恢復
 
co_await promise.yield_value(1); // 第一次恢復后就會停在這里
co_await promise.yield_value(2); // 第二次恢復后就會停在這里
 
final-suspend:
co_await promise.final_suspend(); // 第三次恢復后就會停在這里
}

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

然后在主函數處調用 g.move_next() , 進而恢復了協程之后, 協程就會從剛剛暫停的 co_await 那一行恢復運行.

對了, 過了最后的 final_suspend() 以后, 這個協程就會析構掉. 再次恢復協程就會導致 segmentation fault.

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

2、co_return

co_return 相當于調用了 promise.return_value() 或者 promise.return_void() 然后跳到 final-suspend 標簽那里. 也就是說這個這個協程結束了, 再也無法被恢復了.

而對比 co_yield 調用的是 co_await promise.yield_value(). 他們的區別就是 co_yeild 完了協程繼續等著下一次被恢復 , co_return co_return完了協程就結束了. (為了讓協程也能像普通函數一樣返回)

我們來看一段代碼.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#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(); // 這時才從 initial_suspend 之中恢復, 所以就叫 lazy 了
    cout << g.get();
}

由于這個協程只能被恢復一次, 所以我稍稍修改了一下 lazy 的實現. 可以參考這里:

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

到此這篇關于C++20 特性 協程 Coroutines的文章就介紹到這了,更多相關C++20 協程 Coroutines內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://zhuanlan.zhihu.com/p/349210290

延伸 · 閱讀

精彩推薦
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
主站蜘蛛池模板: 国产亚洲欧美日韩在线观看不卡 | 国产精品一区免费在线观看 | 国产精品成人av片免费看最爱 | 小雪奶水翁胀公吸小说最新章节 | 最新午夜综合福利视频 | 欧美日韩精品一区二区三区蜜桃 | 久久国产在线观看 | 国产日产精品久久久久快鸭 | 久久精品一区二区三区四区五区 | 欧美一级在线免费 | 轻点插视频 | 亚洲精品成人久久久 | 久久草在线视频国产 | 黄色毛片一级视频 | 国产精品.com | 黄网站在线免费 | 国产亚洲精品久久久久婷婷瑜伽 | 欧美一级黄色影院 | arabxxxxvideos| 黄色免费影片 | 亚洲国产成人久久一区www妖精 | 黄色片网站免费观看 | 亚洲人成在线播放网站 | 西川av在线一区二区三区 | 高清国产在线 | 一级免费特黄视频 | 国产正在播放 | 91久久国产露脸精品国产护士 | 国产精品99久久久久久宅女 | 欧美雌雄另类xxxxx | 亚洲国产一区二区三区 | 91九色网址| 香蕉久久久久 | 亚洲福利在线观看视频 | 性生活视频网站 | 日韩美香港a一级毛片免费 日韩激情 | 精品久久久久久 | 中国免费黄色 | 在线免费视频a | 男女隐私免费视频 | 成人免费乱码大片a毛片视频网站 |