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

为什么只有 go 语言原生支持协程?

  •  1
     
  •   wmwm · 2023-08-10 13:27:31 +08:00 · 6867 次点击
    这是一个创建于 471 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大部分语言的协程都是基于 epoll/select 机制,由于 epoll/select 只能处理 IO 问题,所以导致大部分语言的协程也只能处理 IO 问题

    而 go 语言的协程可以在任何情况下使用,是真正意义上的协程,底层是通过汇编指令 pop/push 保存上下文

    以上理解正确吗?

    46 条回复    2023-08-12 18:37:30 +08:00
    nightwitch
        1
    nightwitch  
       2023-08-10 13:31:42 +08:00 via Android   ❤️ 2
    把协程的实现放到库来做还是放到语言特性里只是语言设计者的偏好问题。协程和 epoll 也是正交的关系,没什么基于不基于的。
    thinkershare
        2
    thinkershare  
       2023-08-10 13:34:09 +08:00
    因为协程并不是什么先进值得被其它语言吸收的好东西,纯粹是语言创建者的个人偏好。
    coderxy
        3
    coderxy  
       2023-08-10 13:39:25 +08:00   ❤️ 1
    因为现在流行的几个语言里面,就属 go 诞生的最晚,其它语言有一定的历史包袱。
    tulongtou
        4
    tulongtou  
       2023-08-10 13:43:16 +08:00
    Java 也原生支持了
    emSaVya
        5
    emSaVya  
       2023-08-10 13:43:18 +08:00   ❤️ 8
    你可能需要重新理解协程的概念。

    另外应该是 2015 年前后 大批程序设计语言都上了 coroutine, 比如 c# js python 。什么概念时髦了就上什么。

    c++ coroutine 讨论了得有几十年了, 现在 c20 也加上了。
    duke807
        6
    duke807  
       2023-08-10 13:48:11 +08:00 via Android
    各语言协程的底层都是通过汇编指令 pop/push 保存上下文
    lovelylain
        7
    lovelylain  
       2023-08-10 13:52:06 +08:00 via Android
    @duke807 async/await 这种是状态机
    agagega
        8
    agagega  
       2023-08-10 15:24:19 +08:00 via iPhone   ❤️ 1
    协程和用户态线程是不同的东西,只是 go 利用特殊设计在后者实现了前者的要达到的效果。Swift 的并发支持也有点异曲同工之妙
    sunny1688
        9
    sunny1688  
       2023-08-10 16:02:40 +08:00   ❤️ 2
    yield 就是协程,很多语言都支持协程的,只不过没有语言层面实现调度器(运行时)
    touchmii
        10
    touchmii  
       2023-08-10 17:03:34 +08:00
    就一个 runtime 的事情, 想支持啥特性都可以, 至于 go 为什么要放到语言特性中来自然是要宣传造势, 这是它最显著的特点.
    kenvix
        11
    kenvix  
       2023-08-10 17:48:39 +08:00
    先问是不是,再问为什么。
    GeekGao
        12
    GeekGao  
       2023-08-10 17:54:54 +08:00
    x y problem
    oldshensheep
        13
    oldshensheep  
       2023-08-10 18:07:13 +08:00
    golang 的协程有两种抢占式和协作式,抢占式类似于 Java 的虚拟线程,协作式类似于 yield
    支持类似于 Golang 的抢占式的协程的语言有 Java ,其他的语言好像不怎么支持
    nuk
        14
    nuk  
       2023-08-10 19:55:29 +08:00   ❤️ 4
    golang 从来都没有协程,goroutine 本质上是 M:N 的用户线程。
    官方的说法:
    A goroutine is a lightweight thread managed by the Go runtime.
    WebKit
        15
    WebKit  
       2023-08-10 20:03:24 +08:00 via Android
    Kotlin 也支持
    ravenl
        16
    ravenl  
       2023-08-10 20:08:43 +08:00
    go 的协程还没出啊
    https://research.swtch.com/coro
    AItsuki
        17
    AItsuki  
       2023-08-10 21:05:42 +08:00   ❤️ 4
    goroutine 确实不是协程。协程最关键的一点是非抢占式调度,可以显式的挂起和恢复,而不是阻塞。
    ikas
        18
    ikas  
       2023-08-10 22:20:47 +08:00
    不正确
    wangritian
        19
    wangritian  
       2023-08-11 01:42:40 +08:00
    不懂,但 goroutine+channel 做 IO 并发真的超爽
    lightjiao
        20
    lightjiao  
       2023-08-11 04:19:37 +08:00 via iPhone   ❤️ 1
    yulon
        21
    yulon  
       2023-08-11 07:16:07 +08:00
    你完全说反了,Go 的底层就是 Epoll/IOCP/Kqueue ,你就算不使用网络功能,它默认就是用的 net poll ,因为它就是发明来解决网络问题的语言😅
    holulu
        22
    holulu  
       2023-08-11 08:28:39 +08:00
    Go 诞生的目的是为了提升编写多核及网络应用的效率,其特性的设计肯定就围绕这个目标的。
    hankai17
        23
    hankai17  
       2023-08-11 08:32:51 +08:00
    我认为 任何语言都可以实现自己的协程
    但是很少有像 go 那样 自带协程调度器
    还有我认为 抢占/非抢占 区别就是主动/非主动的'yeild'当前上下文 对于协程实现来说 不是特别难
    所以我认为 原生支持协程 原生就意味着 有协程调度器
    lysS
        24
    lysS  
       2023-08-11 09:15:22 +08:00
    go 的调度就是依赖系统的异步模型啊,这样可以在 go 里面用同步代码写异步逻辑,而不用一堆回调;像文件 io 没有异步模型,go runtime 只有锁定 M
    tyrantZhao
        25
    tyrantZhao  
       2023-08-11 09:43:10 +08:00   ❤️ 1
    只有 go 自带调度器了
    weishao2011
        26
    weishao2011  
       2023-08-11 10:07:36 +08:00
    1 、epoll/select:确实,很多基于协程的异步 I/O 库利用了 epoll (在 Linux 上)或 select 来进行非阻塞 I/O 操作,以支持高并发。这些机制主要处理 I/O 问题,如网络通信、文件读写等。

    2 、大部分语言的协程只能处理 IO 问题:这不完全正确。协程的本质是一种轻量级的线程抽象,用于支持高效地切换执行上下文。而是否主要用于 I/O 取决于其使用场景和设计目的。有些系统和库的确重点强调了 I/O ,但协程本身的设计和实现并不局限于此。
    githmb
        27
    githmb  
       2023-08-11 10:18:13 +08:00
    那 Rust 就不算原生支持了吗
    Huelse
        28
    Huelse  
       2023-08-11 10:22:11 +08:00
    实际上都是跑在系统线程上的,只是调度方式不同罢了。
    cosmain
        29
    cosmain  
       2023-08-11 10:34:05 +08:00   ❤️ 1
    cosmain
        30
    cosmain  
       2023-08-11 10:40:26 +08:00
    IO 多路复用需要系统底层提供支持来实现一个线程中监控多个文件描述符的 IO 事件,但是通常你去真正读写的时候,还是会打开新的线程。

    协程是一个语言层面的抽象,只需要语言提供库和特性,和系统底层无关,通常是用户态的“技巧”,旨在在一个线程中处理多个“task”,达到减少内核线程存储、上下文切换等开销。
    mmdsun
        31
    mmdsun  
       2023-08-11 11:36:51 +08:00
    正意义上的协程??到现在协程本身没有一个统一的定义吧,而是一个广泛的概念。
    https://en.wikipedia.org/wiki/Coroutine

    其实用汇编也能做到的,不一定需要基于 epoll/select 机制。
    而且这个也不是异步 IO 。IOCP 、Linux 5.1 以后的 io_uring 了解下。

    协程和 IO 多路复用关联不是很强。通常说协程是一种编写并发代码的方式,可以在某些点暂停和恢复它们的执行不阻塞主线程。

    你用 goto 、lablel 跳转+状体机写,也能算 coroutine 吧?( stackless coroutine )
    chesha1
        32
    chesha1  
       2023-08-11 13:24:04 +08:00
    C++20 也有协程了,也是任何条件下可以用的函数
    wanguorui123
        33
    wanguorui123  
       2023-08-11 13:27:33 +08:00
    协程、虚拟线程、事件队列,实现目的都类似
    wonderblank
        34
    wonderblank  
       2023-08-11 14:37:28 +08:00
    > 大部分语言的协程都是基于 epoll/select 机制,由于 epoll/select 只能处理 IO 问题,所以导致大部分语言的协程也只能处理 IO 问题

    不正确,协程和 epoll 不是一个东西,也不是说基于什么。你可以理解为协程是香蕉,epoll 是体重。epoll / select / kqueue 是多路复用的实现方案而已。可以配合协程使用。


    > 而 go 语言的协程可以在任何情况下使用,是真正意义上的协程,底层是通过汇编指令 pop/push 保存上下文

    不正确,首先没办法定义`真正意义上的协程`是什么,以及底层基本上都一样。

    > 以上理解正确吗?

    全部不正确

    基本上可以用任何语言实现协程。

    @wanguorui123

    > 协程、虚拟线程、事件队列,实现目的都类似

    不正确。设计他们,以及背后的原因都各不相同。对于特定的问题没有银弹,所以出现了很多方案。
    w950888
        35
    w950888  
       2023-08-11 14:42:18 +08:00
    @WebKit kotlin 标准库不支持协程
    oxromantic
        36
    oxromantic  
       2023-08-11 14:45:36 +08:00
    lua 是在 bytecode 层面的协程,用的 longjmp ,完全不用线程等机制
    zjsxwc
        37
    zjsxwc  
       2023-08-11 14:59:20 +08:00
    完整的协程,php8.1 也有
    https://www.v2ex.com/t/953310#reply0
    vizee
        38
    vizee  
       2023-08-11 15:01:12 +08:00
    先问是不是,再问为什么
    wanguorui123
        39
    wanguorui123  
       2023-08-11 16:00:03 +08:00
    @wonderblank 设计目的都是为了高吞吐量,榨干系统性能
    luoqeng
        40
    luoqeng  
       2023-08-11 16:29:20 +08:00
    问题的关键应该是协程调度器实现
    其他语言库一般用来处理网络 io ,只是利用 epoll/select 来实现调度器,Go 没有依赖 io 复用来实现调度器
    wetalk
        41
    wetalk  
       2023-08-11 16:34:26 +08:00
    回到线程的定义:操作系统能够进行运算调度的最小单位。协程不是必须的
    wonderblank
        42
    wonderblank  
       2023-08-11 17:41:15 +08:00
    @wanguorui123

    > 设计目的都是为了高吞吐量,榨干系统性能

    你年纪太小了,也不能说你错。
    charlie21
        43
    charlie21  
       2023-08-11 17:46:36 +08:00
    Golang 采用的 CSP 模型和 Erlang 的 Actor 模型
    https://zhuanlan.zhihu.com/p/27341488
    julyclyde
        44
    julyclyde  
       2023-08-11 19:44:20 +08:00
    能提出这种问题,说明思维方式有问题
    bthulu
        45
    bthulu  
       2023-08-12 16:33:46 +08:00
    epoll 又不是什么牛逼机制, 纯属微软作死导致 epoll 在服务端一家独大. 你可以试试 iocp, 比 epoll 强多了, 偏偏支持的没几个.
    inshua
        46
    inshua  
       2023-08-12 18:37:30 +08:00
    erlang 早就有了,那几年 erlang 火了一阵,后来实在太非主流了用的就少了,但是设计思想传播出去了就有了一堆衍生,当然协程出现的更早,但是在并发领域用协程 erlang 应该算是早的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2930 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 132ms · UTC 00:13 · PVG 08:13 · LAX 16:13 · JFK 19:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.