Go routines 和 Java 线程池的区别有哪些?

2019-11-01 08:26:39 +08:00
 lhx2008
比如使用上,原理设计上
6450 次点击
所在节点    Go 编程语言
33 条回复
JohnSmith
2019-11-01 16:13:13 +08:00
协程 vs 线程
optional
2019-11-01 18:19:21 +08:00
@lhx2008 『用户态调度,是不是可以抽象理解为把阻塞代码前后进行分割,分成小的代码块,放入线程池中执行呢』
原理上是这样没错,但是有个重要的区别:
协程:主动放弃 cpu
线程:抢占式调度
所以,对于 io 型应用来说,前者少了很多后者『抢到了 cpu,发现数据还没准备好,又放弃 cpu 』的消耗。
Les1ie
2019-11-01 18:26:58 +08:00
楼上的一个线程跑满吃掉一个 CPU,多个线程多个 CPU 似乎是没分清线程和进程的区别
如果我没理解错的话,进程一次只能在一个 CPU 上,进程可以调度到其他 CPU 上,但是一次只会在 1 个地方 而线程,是运行在进程的上下文中的。
lhx2008
2019-11-01 19:19:13 +08:00
@optional #22
抢占式调度怎么理解呢,通常来说,支持抢占式不是更好吗? Go 不是也有说要支持抢占式调度的协程。

您说的 『抢到了 cpu,发现数据还没准备好,又放弃 cpu 』 是在有锁的情况下才会争抢吧?而且像 Java 的 AQS,抢不到锁也是直接排队等待解锁了。争抢的情况似乎也不是很明显。

我的理解只是这样调度的粒度更小,所以效率提高了。
lhx2008
2019-11-01 19:22:38 +08:00
@optional #22
如果是说线程和 CPU 数量相同,可以减少线程之间被 CPU 核切换。那其实线程池也是可以配置的。
vkhsyj
2019-11-01 20:28:28 +08:00
线程池还是靠 OS 来调度线程,go 的携程是语言内置的调度器来调度,当然还是受操作系统调度(
optional
2019-11-01 20:31:11 +08:00
@Les1ie 『线程是 cpu 调度的基本单位』。
@lhx2008 协程理论上不需要抢占式调度,而且理论上协程不可中断(没有 handle 指向它),除非你把他的执行线程干掉,至于为什么搞出个抢占式的,我只能这么理解『为了防止某个协程一直不主动释放,导致其它饿死』,实现也是怪怪的,做的太多就变成"用户态线程了"。
至于为什么说协程调度效率高:对于线程调度来说,就是给自己打个 runnable 的标记,然后等调度器赏光,但是对于调度器大爷来说,它不理解你的任务细节,每次给你 300 个时间单位,你可能 10 个单位时间就干完了,剩下的 290 只能还回去(对于 IO 密集型应用这很常见),但是对于协程来说,我干完了可以把这机会给兄弟们用,直到用完这 300 个单位。
线程池没有调用栈,做不到协程的效果。
lhx2008
2019-11-01 20:40:36 +08:00
@optional #27 谢谢,你讲的微观细节很透彻,我从来没考虑过,不过真的有这么多时间片被浪费了吗
secondwtq
2019-11-01 21:13:48 +08:00
@lhx2008 "粒度更小"一般对应的是”更加灵活“与”效率更低“
当然不是说一刀切效率就一定更高,一刀切效果好的前提是切的地方准确

协作式多任务就正好满足这一条件,进程在明确自己不需要 CPU 时放弃 CPU,而在做事情时操作系统不来烦你,实际就保证了 CPU 一直都在做有用的事情
可以类比在你写代码时没事总是来催你的产品,没事总是来找你”支持“的队友

缺点是如果你完成了任务一直不汇报,那整个项目就 block 在你这了
我认为现代编程语言(好吧 ... 强行把 Go 称为现代编程语言也过得去)对这个问题提供了很好的抽象,所以现在才有这么多人觉得这么好用。这在 Windows 3.1 和 MacOS Classic 那个年代大概是难以想象的吧
qiyuey
2019-11-01 21:44:51 +08:00
halo117
2019-11-02 02:50:13 +08:00
@optional 如果只是协作式确实有阻塞其他协程饥饿问题,但 goroutine 调度目前是有 timeout 检测一旦阻塞过久会尝试主动让出执行权,只是让出时机是在特定情况下,并不全面。所以现在看 goroutine 有点半(伪)抢占的意思。后面版本据说会更进一步做成抢占式,不过这样竞态问题会更频繁?
lhx2008
2019-11-02 08:15:32 +08:00
@halo117 现在的意思好像是,死循环,长时间阻塞到内核态这种情况,就会把执行这个协程的线程扔出调度器?相对于是自动分开一个快的调度线程池和一个慢速线程池。
reus
2019-11-02 10:57:08 +08:00
1.14 版本将会加入基于信号的抢占调度,所以 goroutine 从来就不是协程,它的定义就是并发执行的单元,只不过早期的实现是协作式的。在加入函数入口处的抢占检查之后就已经是半协作半抢占调度了,现在要加入完备的抢占调度了。goroutine 的语义也一直和线程接近而不是协程。什么时候调度,程序员没法判断,并发访问内存也需要上锁,就是当作线程来用的,就是一种用户态线程的实现。

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

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

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

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

© 2021 V2EX