V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
PiersSoCool
V2EX  ›  Go 编程语言

协程真正的作用是什么

  •  
  •   PiersSoCool · 2020-09-09 14:46:03 +08:00 · 6013 次点击
    这是一个创建于 1296 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这里我想出个判断题,看看大家想的是不是一致的

    相对于线程来说,假设有很多请求,假设是 Golang 语言

    1. 协程处理 IO 很快
    2. 协程处理高并发很快
    3. 协程上下文开销很少
    4. 协程占用资源很少

    这里面是都是正确的吗?

    28 条回复    2020-09-10 14:02:50 +08:00
    hdbzsgm
        1
    hdbzsgm  
       2020-09-09 14:49:55 +08:00   ❤️ 2
    不加场景的话 严格说都不对 协程可以提一句 降低上下文切换频次
    misaka19000
        2
    misaka19000  
       2020-09-09 14:50:00 +08:00   ❤️ 2
    以前是为了充分利用 CPU 资源(在现代操作系统出现之前);
    现在是为了简化异步操作,提升程序员编写代码的简洁性;
    OwO233
        3
    OwO233  
       2020-09-09 14:50:34 +08:00
    https://www.zhihu.com/question/20511233
    谷歌搜到了知乎的湖大,感觉很牛,尼康康。OwO
    caskeep
        5
    caskeep  
       2020-09-09 14:52:40 +08:00 via iPhone
    @misaka19000 #2 +1 切换到 go 就是为了异步处理省事好写...
    ysc3839
        6
    ysc3839  
       2020-09-09 15:05:05 +08:00
    感觉网上讨论“协程”更多是指有调度器的,单线程执行的协程。我自己没有接触过,并不太了解。
    我自己最早接触协程是 Python 和 JavaScript 的 async fuction 和 generator,这里的协程是一种可以中途返回,后面再恢复执行的函数,现在 C++ 20 的协程也是属于这一类。
    如果讨论是后面这种协程的话,那处理 IO 快不快、上下文开销大不大,完全取决于怎么实现的,用户可以用线程池+同步 IO 。
    wysnylc
        7
    wysnylc  
       2020-09-09 15:23:54 +08:00
    把多线程能解决的问题搞复杂,把多线程不能解决的问题解决掉
    FutherAll
        8
    FutherAll  
       2020-09-09 15:32:17 +08:00
    goroutine 就是减少频繁的线程上下文切换带来的 CPU 消耗吧,以及 goroutine 占用的内存空间更小。
    G-P-M 模型,真正需要切换线程是在 M 的切换,G 的切换是 go 在用户态实现的调度器来做的,更像是一种用户态线程。
    lambdaq
        9
    lambdaq  
       2020-09-09 15:40:06 +08:00
    真正的作用,那就是处理能力分时啊。。
    noe132
        10
    noe132  
       2020-09-09 15:54:57 +08:00 via Android   ❤️ 1
    协程是单线程异步,你不需要处理锁的问题
    keepeye
        11
    keepeye  
       2020-09-09 16:28:04 +08:00
    goroutine 是多线程协程,能并行的,跟别的语言的单线程协程还是有很大不同的
    PiersSoCool
        12
    PiersSoCool  
    OP
       2020-09-09 16:34:40 +08:00
    感谢大家。

    感觉协程在 JS 来说可能语法上更加方便。

    我可能看了一些奇怪的文章,说协程能提升 IO 速度搞得我十分困惑。我是觉得 IO 问题是多路复用解决的。
    PiersSoCool
        13
    PiersSoCool  
    OP
       2020-09-09 16:35:34 +08:00
    我感觉 go 的协程可以解决一些问题,创建 1000 个线程任务的代价和创建 1000 个协程任务(可能只有几个线程)的代价是不一样的,上下文大部分情况应该都是相同的,上下文切换次数也是不一样的。
    zhangfeiwudi
        14
    zhangfeiwudi  
       2020-09-09 16:38:03 +08:00
    协程上下文开销很少
    协程占用资源很少
    这两对的
    在这两个的前提下 ,才有“协程处理高并发很快”

    因为携程切换都是在用户态进行,所以速度很快,而且每次切换保留现场的资源很少
    PDX
        15
    PDX  
       2020-09-09 16:42:39 +08:00 via iPhone
    我觉得解决回调地狱是最大的作用
    lbp0200
        16
    lbp0200  
       2020-09-09 16:48:37 +08:00
    协程处理 IO 很快

    一百万 IO 任务需要处理
    用线程解决,同时只能启动 1000 个
    用协程解决,同时只能启动 100000 个

    你猜,最后哪种解决方案先完成,先完成的解决方案,算不算快?
    PiersSoCool
        17
    PiersSoCool  
    OP
       2020-09-09 17:17:54 +08:00
    @lbp0200 我觉得在这两个处理 IO 速度是一样的,Golang 里协程也是通过线程实现的,最后都要走到系统调用上,假设采取的都是同样的 IO 技术,无论到没到达系统 IO 瓶颈,速度都是一样的,甚至说,线程还能比协程快一些。以我的了解,像这种阻塞的系统调用,并发启动 100000 个协程 = 启动 启动 100000 个线程,会爆炸的。不知道是不是对的。
    linw1995
        18
    linw1995  
       2020-09-09 19:32:23 +08:00
    1. 用协程也是有用到锁的时候
    2. 协程减少了上下文切换的时间
    3. 用起来更方便,可以控制每个协程的状态(有的实现例外)
    optional
        19
    optional  
       2020-09-09 19:40:12 +08:00 via iPhone
    减少线程的上下文切换。
    JaguarJack
        20
    JaguarJack  
       2020-09-09 21:26:06 +08:00 via iPhone
    3 是重点 用户态的切换开销小
    Aoang
        21
    Aoang  
       2020-09-09 22:11:38 +08:00 via Android
    @PiersSoCool 10w 协程不可能等同于 10w 线程。
    IO 堵塞的时候,线程切换的开销多大?这还没提机器,创建线程的资源大小能和协程相比么…

    如果不考虑多路复用,协程会吊打线程。实际应用中,因为多路复用会导致两者差异并不是非常大。
    但是如果线程与线程之间需要传输资源…差距又会被拉很大。
    而且线程是比协程复杂的多的…玩的不好……就不用说了
    cmdOptionKana
        22
    cmdOptionKana  
       2020-09-09 22:19:56 +08:00
    @PiersSoCool 不对,Golang 只是在有需要的时候自动开线程以充分利用 CPU,协程与线程不是一比一,而是多比一。
    Acoolda
        23
    Acoolda  
       2020-09-09 23:14:33 +08:00 via Android
    就是合集利用 io 等待时间来执行其他任务,让 cpu 一直处于忙的状态
    hakono
        24
    hakono  
       2020-09-09 23:31:00 +08:00   ❤️ 1
    说个我的看法
    我觉得只需要记住一点就行了: 协程不能改善计算密集型任务的执行效率,但却能极大改善大量需要 白 白 干 等 的任务(典型就是 IO 密集)的执行效率

    其实在这里是有一个界限的,线程数量少的时候其实用多线程还是协程差别没那么大。但是当线程非常多,切换开销过大时,协程的优点才会显现,1,2,3,4 才会大致成立(比如大量 IO 请求)

    其实对人类来说,协程才是我们最熟悉、用得最多的处理事情的逻辑。不如说大部分时候我们都是在用协程的思维在处理事情的。即便是对编程一无所知的大妈,平时做饭时都在用协程思维:在烤面包的空档去切菜做其他事情,而不是烤面包的时候就在那干等面包烤完。这就是典型的协程思维。 烤面包需要好几分钟,这好几分钟是白白干等的,相当于做 IO 请求( IO 请求和 cpu 处理速度相比是非常慢的)。这 IO 请求的空档 cpu 完全能去做其他事情而不是在那干等。

    所以理解到这层之后,就会发现我们生活处处都是在用协程做事。然后什么情况协程比线程更好也能比较好判断了
    hakono
        25
    hakono  
       2020-09-09 23:31:55 +08:00
    对了,追加个 go 协程相关的,这个上面已经有人说了:

    讨论协程要区分一般意义上说的协程 和 go 语言的协程( goroutines )
    一般意义上单线程里跑的协程是无法利用多个核心的资源的(毕竟开再多协程都是在一个线程里跑,没法利用多个核心)。解决办法就是开几个线程,把协程分配到这些线程里执行,这样就能完全利用 cpu 了

    go 语言的协程就是后者,在协程的基础上自带调度器,会根据需求创建多个线程然后把协程分配到不同线程里运行,从而利用多个核心。写代码的人根本不用去纠结该开几个线程,每个协程该怎么调度
    reus
        26
    reus  
       2020-09-10 04:08:53 +08:00
    goroutine 又不是协程。
    12101111
        27
    12101111  
       2020-09-10 09:41:20 +08:00
    1.协程处理 IO 很快
    协程不会让 IO 操作变快,但是可以在等待 IO 操作时执行其他协程,也就是提高了吞吐量
    2.协程处理高并发很快
    同 1, 吞吐量大了才能处理高并发
    3.协程上下文开销很少
    相较于 fork join 模式,线程池+有栈协程的开销更小,但是 Rust 的无栈协程才是无开销的模式
    创建线程需要操作系统分配完整的栈+TCB, 而创建有栈协程只需要分配较小的栈+运行时自己调度,无栈协程在编译时直接变成状态机,运行时建立这个状态机就一直执行下去了,不需要分配栈.
    4.协程占用资源很少
    有栈协程会分配一块内存作为栈,比线程省资源
    chaleaoch
        28
    chaleaoch  
       2020-09-10 14:02:50 +08:00
    234 吧.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2718 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 00:21 · PVG 08:21 · LAX 17:21 · JFK 20:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.