• C++20协程解糖


    本文的前置知识:你至少要知道其他语言的无栈协程是如何实现的,如C#,python。lua不算,lua实际上是有栈协程(对lua虚拟机有栈)

    如果你看到这行文字,说明这篇文章被无耻的盗用了(或者你正在选中文字),请前往 cnblogs.com/pointer-smq 支持原作者,谢谢

    编译时:

    1. 编译器转写每个coroutine,生成一个thunk和一个state_object
      1. thunk为了保持调用的语法,封装创建state_object和返回值的事情
      2. coroutine本体转写为state_object对象,转写的样子可以见上一篇文章。state_object有一个promise成员,类型为 协程返回值::promise_type
    2. 将所有coroutine的调用处都改为调用thunk函数

    运行时:

    1. 调用thunk,thunk干这些事
      1. coro = new state_object
      2. coro.promise.get_return_object() 但不着急返回(后文称此对象为future)
      3. 启动coro
      4. 返回(2)得到的东西(这里其实有rvo,因此不要求get_return_object的拷贝构造)
    2. coro启动后的流程
      1. 通过 await promise.initial_suspend() ,协程库编写者可以指定协程是否在刚启动时就立即暂停
        1. 一般给generator用,等到真的在迭代generator的时候,恢复协程执行真正的逻辑,一个简单的lazy
        2. 也可以给其他异步函数用,等到await的时候才启动异步逻辑,而不是调用时就启动
      2. 从(1)恢复后,执行coroutine真正的逻辑
        1. return语句会被转换为 promise.return_value 或者 promise.return_void ,令另一边的future拿到协程的返回值
        2. await obj 会暂停协程,并将协程句柄交给obj,等obj通过此句柄再唤醒自己(后面细说)
      3. (2)完成后,执行 await promise.final_suspend() ,协程库编写者可以指定协程在执行完用户逻辑后暂停,来避免一些资源共享的问题
        1. 一般来讲generator在这里暂停,因为generator要轻量,所以generator和协程这端的promise之间没有new出来的共享状态,generator直接引用协程,因此协程随generator销毁,而非执行完后自动销毁,否则就会出现generator引用了已经销毁协程的问题
        2. 如果future和promise之间有共享的shared state,那final suspend可以不暂停,promise随协程销毁,future仍然持有着shared_state及其中的返回

    单独说一下协程 r = await future 的流程

    1. 检查 future.await_ready() ,若true,则表明future已经完成,协程不需要暂停(语义上是暂停立即恢复),跳到(3)
    2. 否则通过 future.await_suspend(handle) 将协程句柄交给future
      1. future.await_suspend 的返回值可以是协程,那么调用会恢复这个协程(切过去),这就是前面提到的再await时才启动的协程的启动位置
      2. future.await_suspend 返回值也可以是bool,当bool是false的时候不暂停协程,直接跳到(3)
      3. 返回其他值的,忽略并暂停协程
    3. 从(2)中resume之后,调用 r = future.await_resume() ,取出来future中的返回值

    一些重点:

    1. 协程端持有promise,调用者持有future,协程通过promise给future传送结果
    2. promise和future需要新增一些接口以供C++20协程使用
      1. Future<T>::promise_type 指定本future类型对应的promise
      2. promise.initial_suspend / final_suspend
      3. promise.get_return_object() 返回自己对应的future给调用者
      4. promise.return_value / return_void 看情况提供
      5. future.await_ready / await_suspend
      6. future在完成后记得调用 await_suspend 传进来的 handle.resume()
    3. 协程可以在启动前和结束后额外暂停两次,由promise控制

    一些细节:

    1. promise.initial_suspendfinal_suspend 通常返回的是C++自带的awaitable:suspend_alwayssuspend_never
    2. 如果coroutine中出现未处理异常,会调用 promise.unhandled_exception() ,然后直接进入 final_suspend
    3. yield会被转换成 promise.yield_value
    4. coroutine会在 final_suspend 后自动销毁,也可以由 coroutine_handle.destroy() 手动销毁
    5. 所谓 coroutine_handle 就是new出来的state_object指针包一下,没有引用计数,也没有禁用拷贝,析构也不会自动delete,注意别泄露和悬挂
    6. promise实际上就是state_object的成员,而state_object的地址就是 coroutine_handle,所以给定一个promise引用,他可以通过取地址增减偏移量的方式,和 coroutine_handle 互相转换,通过 coroutine_handle::from_promise / coroutine_handle:promise

    Snipaste_2020-05-10_15-33-44

  • 相关阅读:
    寒假学习进度-14(疫情)
    寒假学习进度-13(Python自然语言处理)
    寒假学习进度-12(热词展示)
    寒假学习进度-11(词云图的使用)
    寒假学习进度-10(pyecharts的下载和使用)
    寒假学习进度-9(spark streaming编程初级实践)
    寒假学习进度-8(热词爬取)
    寒假学习进度-7(Python爬虫)
    寒假学习进度-6(Python连接MySQL数据库)
    寒假学习进度-5
  • 原文地址:https://www.cnblogs.com/pointer-smq/p/12863478.html
Copyright © 2020-2023  润新知