某些语言的协程机制,其作用是什么,是否会造成额外的开销

2020-08-31 09:36:30 +08:00
 maxxfire
一般情况下线程就够用了,线程也是系统的最小调度单位。
有些语言又提供了协程机制,那么在运行的时候不是还要多开一个线程(调度器或虚拟机)来调度这些协程?这不是一种资源的浪费吗。当然有些简单的协程可能直接用 siglongjmp 堆栈还原来实现了。
像传统 C/C++,直接编译机器码,直接就跑了,简单粗暴,还整什么调度器。
6188 次点击
所在节点    程序员
51 条回复
fasionchan
2020-08-31 17:42:27 +08:00
引入协程是为了解决 IO 阻塞问题,在高并发场景你不可能每个连接都分配一个线程去处理,这时协程的作用就体现出来了。协程比线程更加轻量级,占用资源更小。开销的话看实现,一般协程不需要很复杂的调度,在 IO 不可用时让出执行权,在 IO 就绪时重新执行,开销相对而言并不大。对于计算密集型场景而言,协程并没有什么用处,相反是个累赘。
tlday
2020-08-31 17:45:13 +08:00
我觉得 golang 的现状可能是因为开发组写 C++发现多线程加异步太费脑子掉头发。总结规律以后发现“用传递消息来共享数据,而不靠共享数据来传递消息”可以完美解决各种锁 /同步问题,减轻心智负担。

但是如果这样做,就会导致本来不需要 IO 的场合平添了 IO,如果简单用线程实现,会有大量的 CPU 时间片被用在等待 IO 里,一个数据从这个线程到队列里面再到另一个线程,时间和资源全浪费在线程调度,上下文切换,内核 /用户态切换,Cache 页置换上了。

这个时候就发现需要“框架”来接管这一层,这一层把线程间的 IO 改为线程间内存共享,对上一层屏蔽内部实现来减轻“框架”使用者的心智负担,做了这个“框架”之后,会发现 C++的语法来实现这些东西过于丑陋了,而且需要一种机制来确保编译期就把一些“数据被传递给另一个线程之后又被当前线程继续使用了”这种“框架”不能容忍的情况报错。于是干脆出了一个编程语言。

如果你在做优化时,发现 golang 的协程对你的应用场景已经太重,那么你完全可以用没有“虚拟机”的语言来拿“心智负担”换运行速度,golang 只是提出了一种当前很多场景下更好的解决方案与设计理念,填补了“多 IO 场景下低心智负担的多线程编译型语言”这一空白。我记得实际上协程在 golang 出现之前是日渐式微的,因为自己实现调度算法大部分时间并不如操作系统内核的调度算法高效,有点类似于自己用 volatile 不如 gcc 的编译器优化高效,所以现在已经不推荐这么做一样。

上面回答的比我专业的多,也没有我这么多“臆测”,但可能没有准确解释楼主的疑问,我就献丑了。
xiaoliu926
2020-08-31 17:47:23 +08:00
终于看到有我 kotlin 的身影了
saberlong
2020-08-31 18:01:28 +08:00
@tlday 有个地方有问题。推荐传递不推荐共享的思想和协程没有关系。之前看到一篇文章,目前 go 写的代码中,还是共享的比例高。
yufpga
2020-08-31 18:27:53 +08:00
首先一点,要知道一台 4 核 4 线程的计算机, cpu 最大只能并行处理 4 个任务, 也就是说最多 4 个线程能同时跑在 cpu 上。假如一个程序,创建了 100 个线程, 那么同一时间只有 4 个线程能跑;如果我创建的是 4 个线程和 100 个协程, 虽然我也只能同时跑 4 个线程, 但是考虑到协程上下文切换以及协程本身所消耗的资源要比线程小很多, 你说哪个划算. 而且不同语言的协程方式实现不一样,并不一定需要和你说的一样需要创建一个线程来专门调度这些协程. 比如有的是栈协程(python 等), 还有 go 语言的 goroutine 调度也并不是专门给协程调度创建一个新的系统线程,而是通过 goroutine 绑定到系统线程(这个系统线程可并不是专门为协程调度服务的)
tlday
2020-08-31 18:49:53 +08:00
@saberlong 我个人觉得 go 用协程主要就是为了解决“线程间消息传递”的性能问题,“线程间消息传递”又是为了解决多线程程序的锁,内存共享,线程同步,内存释放的心智负担问题。绕过协程直接共享在可以自己解决这些问题的况下,确实比传递轻量也简单的多。感谢指正。
janxin
2020-08-31 18:52:42 +08:00
LZ 的疑惑应该是没有弄清楚协程本身的背景。这种本身具有切换逻辑,只是是协程思想的具体实现之一。而调度器只是作为这种实现的一种落地实现方式而已。

https://zh.wikipedia.org/zh-hans/%E5%8D%8F%E7%A8%8B#%E5%90%8C%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%AF%94%E8%BE%83

具体怎么实现和理念并不冲突的,比如协程其实也区分为 stackful coroutine 和 stackless coroutine 。是否包含调度只不过可以理解是根据不同场景的不同取舍或者设计,带有调度器并不是只有 Go 一种语言,另外多种语言也包含了调度器实现,比如 Rust 、Python 。

Go 中比较不一样的点是 goroutine 我个人认为其实是混杂了 fiber 和 coroutine 的实现的,既有用户态线程实现又有 coroutine 的内容,主要是为了解决实现用同步方式编写异步代码的需求。
RubyJack
2020-08-31 19:35:00 +08:00
协程+异步 io 可以模拟出同步阻塞的效果, 减轻人心智负担的同时, 也拥有足够出色的性能
secondwtq
2020-08-31 20:12:59 +08:00
请楼主立刻拆除电脑里所有 GPU 换上 IBM 8514 。

比如说一个现代的 GPU,它需要占一个 PCIe 的插槽(或者 CPU 的 die area ),还要耗费额外的电力,最重要的还要花钱买,,那么这些我认为都是极大的资源浪费。因为 CPU 什么运算都能做,图形运算也可以,现在又单独搞那么一块功能 100% 冗余的 GPU 。
就好比如说,本来你可以直接去市场买菜,却自己去种菜。
littlewing
2020-09-01 03:12:48 +08:00
首先你要了解,线程切换的成本为什么会很高
skinny
2020-09-01 11:47:03 +08:00
我非常好奇某些人举例的几十万、百万协程的使用场景是不是实际经验,还是信口胡诌……

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/702723

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX