V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Yc1992
V2EX  ›  C++

c++ 有没有能够对标 golang 的 goroutine 的协程库啊?

  •  
  •   Yc1992 ·
    bufrr · 2020-10-28 11:48:56 +08:00 · 7057 次点击
    这是一个创建于 1497 天前的主题,其中的信息可能已经有所发展或是发生改变。

    满足 stackful,能够自动 yield 的,有没有? 看了好几个都需要手动 yield 归还 cpu,感觉有点麻烦,希望大佬指点下,不奢求同时有 channel 和 select 了

    44 条回复    2020-10-31 14:32:50 +08:00
    keygen88
        1
    keygen88  
       2020-10-28 11:59:07 +08:00   ❤️ 1
    协程库很多,但很多其它库不支持就比较鸡肋了,这就是个生态问题。
    reus
        2
    reus  
       2020-10-28 12:21:41 +08:00   ❤️ 4
    goroutine 本来就不是协程,对标的就是线程
    ysc3839
        3
    ysc3839  
       2020-10-28 12:23:03 +08:00 via Android
    自动 yield 大概要能插入代码吧? C++ 大概没有这种能力。
    而且 C++20 的 stackless coroutine 出来之后,stackful 的可能会越来越少了。
    abbycin
        4
    abbycin  
       2020-10-28 12:26:21 +08:00 via Android
    libgo 看过没?
    Yc1992
        5
    Yc1992  
    OP
       2020-10-28 12:59:44 +08:00
    @abbycin 谢谢,正在看
    Yc1992
        6
    Yc1992  
    OP
       2020-10-28 13:00:21 +08:00
    @ysc3839 为什么 stackless 更多呢?
    ysc3839
        7
    ysc3839  
       2020-10-28 13:01:46 +08:00
    @Yc1992 因为人们会更倾向于使用语言本身的功能吧?
    Yc1992
        8
    Yc1992  
    OP
       2020-10-28 13:02:11 +08:00   ❤️ 1
    @reus 第一次听到这样的描述
    nightwitch
        9
    nightwitch  
       2020-10-28 13:09:28 +08:00
    zunceng
        10
    zunceng  
       2020-10-28 13:18:31 +08:00
    早几年用 asio coro 玩过
    不过那个上面的 yield 就是对 for loop ... switch case 做了个封装 用起来还是太费脑
    Yc1992
        11
    Yc1992  
    OP
       2020-10-28 13:29:07 +08:00
    @nightwitch 补充一下,必须 c++11 哈
    nannanziyu
        12
    nannanziyu  
       2020-10-28 13:34:26 +08:00 via Android
    @Yc1992
    限定 c++11 的话,微软的 cpprestsdk 里有一个 pplx
    catror
        13
    catror  
       2020-10-28 13:38:30 +08:00 via Android
    用 asio+fiber 写过一个网络程序框架,用着也还行吧
    CRVV
        14
    CRVV  
       2020-10-28 13:55:40 +08:00 via Android   ❤️ 2
    @Yc1992
    如果你去看 Go 的官方文档,里面明确说了这东西不是 coroutine,当然它也不是 thread,所以才起了个新名字叫 goroutine

    大家常说它是协程,是因为它和协程很像,在 io 上也用 epoll, kqueue, iocp 这些系统调用,并且它也确实在 io 操作上 yield 了,老版本上的调度器还不是抢占式的。

    但首先新的调试器已经是抢占式的了,这就不符合协程的定义。然后你对比一下实现的细节,显然现在的 goroutine 和 thread 更接近。

    别人怎么说并不重要,你得去看实际的东西。
    Yc1992
        15
    Yc1992  
    OP
       2020-10-28 15:21:17 +08:00
    @CRVV 不同的调度方式而已,https://golang.org/doc/faq#goroutines 里面明确提到了 coroutine
    reus
        16
    reus  
       2020-10-28 16:20:28 +08:00   ❤️ 2
    @Yc1992 协程当然是协作式调度,但 goroutine 并不限制调度方式,现在的实现就是有抢占式的调度方式,跑着死循环的 goroutine 也可以被抢占,所以 goroutine 并不是协程。
    FAQ 那段话是 11 年前写的: https://github.com/golang/go/commit/d4a4468204448843d0dd15d2d4b89c29607a4a7e
    那时候的 goroutine 确实是协作式调度,没有任何抢占机制。但现在 goroutine 已经不是 11 年前那样了,FAQ 那段话是过时的了。不会有任何协程实现可以有 goroutine 这样的性质,就算可以自动调度,也做不到抢占式调度。只有线程具有抢占式调度的特性,所以说 goroutine 对标线程。
    zzzbkl
        17
    zzzbkl  
       2020-10-28 17:09:52 +08:00 via Android
    试试 boost fiber
    Yc1992
        18
    Yc1992  
    OP
       2020-10-28 17:26:47 +08:00
    @zzzbkl 看了半天,不会用,貌似要手动 yield
    hdbzsgm
        19
    hdbzsgm  
       2020-10-28 17:51:12 +08:00
    folly
    wnpllrzodiac
        20
    wnpllrzodiac  
       2020-10-28 17:56:05 +08:00 via Android
    st?
    sujin190
        21
    sujin190  
       2020-10-28 18:35:54 +08:00
    @reus #16 你确定? go13 的时候写个死循环照样卡死其他 goroutine 啊,啥时候改了抢占式了
    caviar
        22
    caviar  
       2020-10-28 18:50:35 +08:00
    user8341
        23
    user8341  
       2020-10-28 18:57:51 +08:00
    @caviar 这个很新,可能很多人还没用上

    Go 1.14 发布日期是:25 February 2020
    BadMan
        24
    BadMan  
       2020-10-28 19:17:46 +08:00
    我在使用微信的 libco,挺好用的
    hronro
        25
    hronro  
       2020-10-28 19:47:13 +08:00
    居然这么多人说 Go 的 goroutine 不是协程,还有说可以抢占的就不是协程。
    难道不是 stackful coroutine 都可以做到抢占吗? stackless coroutine 确实做不了抢占。
    framlog
        26
    framlog  
       2020-10-28 19:58:06 +08:00
    seastar
    beidounanxizi
        27
    beidounanxizi  
       2020-10-28 20:18:40 +08:00
    @reus 调度方式最新版本确实变了 尤其是 1.10 之后 ,但是 GMP 这一整套逻辑 是不是还是说和其他 coroutine 的差不多呢? 疑问脸.jpg
    reus
        28
    reus  
       2020-10-28 20:29:58 +08:00
    @sujin190 1.14
    reus
        29
    reus  
       2020-10-28 20:45:34 +08:00
    @beidounanxizi 和哪个协程实现差不多?请举出例子,不要相当然。
    hankai17
        30
    hankai17  
       2020-10-29 00:00:41 +08:00 via iPhone
    swapcontext
    reus
        31
    reus  
       2020-10-29 01:03:44 +08:00   ❤️ 1
    另外就普适性而言,和 goroutine 对等的,也是线程。在 go 里,你只能用 goroutine,不存在其他并发单元。而 C/C++
    里,能担当这个任务的,只能是 pthread 这类线程库,而不是其他协程库,因为协程库还没有统一的标准,你用这个,他用那个,没法融合的。
    另外,虽然现在主流 pthread 实现,都是 1:1 映射到系统线程,但是,M:N 映射到系统线程的 pthread 实现,也是存在的。pthread 线程,和 goroutine,都只是并发的单元,具体怎么实现,对于使用者是不可见的。goroutine 历史上曾经实现为纯协作式调度,不代表它就是协程。因为它加入抢占式调度之后,程序也不需要变化,怎么调度都不影响语义,这是协程不具备的性质。协程语义一般都会有确定的调度点,例如 yield 语句、await 语句,而不会是任何地方。
    Balthild
        32
    Balthild  
       2020-10-29 04:04:12 +08:00
    @reus 从普遍意义上讲,「抢占即非协程」似乎不那么绝对。比如,Rust 有抢占式的异步运行时实现,比如 tokio 、smol 等。若按「抢占即非协程」定律,Rust 的异步就不是协程了。但 Rust 异步的设计明确自己是 stackless coroutine 。这需要如何解释呢?
    missdeer
        33
    missdeer  
       2020-10-29 08:52:55 +08:00
    赞同 31 楼。

    早些年,coroutine 概念还几乎不见于日常开发,很多操作系统(比如 DOS,一些 UNIX )还不支持内核线程时,有第三方库(比如 pthread)实现用户态线程及调度,当然后来 pthread 的实现也千变万化。
    fpure
        34
    fpure  
       2020-10-29 09:04:19 +08:00 via Android
    @CRVV 绿色线程?
    reus
        35
    reus  
       2020-10-29 09:21:55 +08:00   ❤️ 2
    @Balthild tokio 并不是抢占式的运行时,tokio 文档里清晰说了的: https://tokio.rs/blog/2020-04-preemption
    你对“抢占”的理解有误。
    reus
        36
    reus  
       2020-10-29 09:28:02 +08:00   ❤️ 1
    @Balthild 划出 tokio 文档里的重点给你:
    A common solution to this problem is preemption. With normal OS threads, the kernel will interrupt execution every so often in order to ensure fair scheduling of all threads.
    os 线程支持抢占
    Runtimes that have full control over execution (Go, Erlang, etc.) will also use preemption to ensure fair scheduling of tasks.
    go 、erlang 也支持抢占
    This is accomplished by injecting yield points — code which checks if the task has been executing for long enough and yields back to the scheduler if so — at compile-time.
    插入调度点,这是 go 过去的做法,现在增加了用信号去抢占的方式
    Unfortunately, Tokio is not able to use this technique as Rust's async generators do not provide any mechanism for executors (like Tokio) to inject such yield points.
    tokio 不支持抢占

    你的大前提错了,推论自然全错。
    CRVV
        37
    CRVV  
       2020-10-29 10:20:28 +08:00   ❤️ 1
    @Balthild
    @hronro

    https://en.wikipedia.org/wiki/Coroutine#Comparison_with_threads
    协程,coroutine,co 是 cooperatively,合作协作的意思。
    相对的概念是 preemptively,抢占

    这是这个 coroutine 原本的意思。

    在一个 stackful coroutine 的实现上加上抢占式调度,那当然可以做。
    但做出来的东西就不叫 coroutine 了。
    CRVV
        38
    CRVV  
       2020-10-29 10:53:49 +08:00
    @Yc1992
    我本来想说的就是这一段。

    When a coroutine blocks, such as by calling a blocking system call, the run-time automatically moves other coroutines on the same operating system thread to a different, runnable thread so they won't be blocked.

    这里说的不是把多个 coroutine 放到同一个线程上来跑。是说如果有一个 coroutine 阻塞住了一个 thread,就把其它的 coroutine 放到别的线程上跑。

    然后有一句话,The programmer sees none of this, which is the point. The result, which we call goroutines, ...

    所以总结一下就是,有一个协程,它 block 住了(比如在 python 的 async function 里面写了一句 time.sleep ),这样其它的协程就不能跑了对吧,那我们写一个高级一点的 runtime,把能跑的协程放到其它 thread 上跑。
    并且把这些过程都隐藏起来,什么 yield 之类的东西都没有了,都是 runtime 里面的事情。

    所以说这是一个基于 coroutine 的想法,或者说是一个经过了大改的 coroutine,还专门说了这个东西叫 goroutine 不叫 coroutine 了。

    所以还是上一条回复里面说的,它改完了就不是 coroutine 了。
    Yc1992
        39
    Yc1992  
    OP
       2020-10-29 11:14:29 +08:00
    @CRVV 我明白你的意思,也不用去纠结名字,总之都是对线程的一个封装
    user8341
        40
    user8341  
       2020-10-29 11:29:04 +08:00
    各位大佬,golang 什么情况下会调用 调度器?

    我知道的有以下这 3 个:
    1. syscall
    2. go 语句
    3. 阻塞读写 chan

    普通函数调用会不会调用 调度器?
    user8341
        41
    user8341  
       2020-10-29 11:31:40 +08:00
    另外,1.14 版加入了时间片,时间片用完 sysmon 会通过发送 signal 给线程的方式,强制一个 goroutine 调用 调度器。
    dollar
        42
    dollar  
       2020-10-29 13:17:05 +08:00 via iPhone
    brpc 中的 bthread 挺好用的
    joydee
        43
    joydee  
       2020-10-29 13:48:18 +08:00
    可以尝试看看 Rangev3 作者 Eric Niebler 用过的 c++ coroutine 库,链接[https://github.com/lewissbaker/cppcoro],遵从 C++ coroutines TS N4680,不过代码还是蛮晦涩的,慎用
    Balthild
        44
    Balthild  
       2020-10-31 14:32:50 +08:00
    @reus 原来如此。也就是说,只有可以真正地打断比如死循环这种阻塞操作的才算是抢占式的,所以抢占的肯定不是协程,这点上没问题了。

    但 async-std 的确也有一个 PR (之前记成了 tokio,看了你贴的文章才想起来)能做到自动检测阻塞,然后把其他协程放在新线程。这样的做法虽然不是抢占式,但也能达到没有确定的调度点、任何地方都可能调度的效果。那么这种运行时要怎么看待呢,是不是严格来讲也不属于协程了?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1106 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 19:06 · PVG 03:06 · LAX 11:06 · JFK 14:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.