1
yuikns 2019-11-01 08:29:32 +08:00 via Android 1
go routine 是官方提供的,线程加协程池
|
2
optional 2019-11-01 08:35:53 +08:00 via iPhone 2
用户态 vs 内核调度
|
3
Orenoid 2019-11-01 08:39:44 +08:00 via Android 1
Go 不只是线程,还包括协程,具体调度原理就不清楚了
|
4
111111111111 2019-11-01 08:41:45 +08:00 via Android 2
1.协程的成本开销比线程小
2.协程是用户调度,线程是交给系统调度 使用上协程应该比线程要多操点心,但 Go 自己有调度器协程使用上其实不怎么操心 其他的,可能就是省了创建线程管理线程的代码,只写一个关键字就行了 |
5
lhx2008 OP |
7
baiyi 2019-11-01 09:05:53 +08:00 1
不清楚 java 的线程池,没办法比较
如果想了解 goroutine,推荐一篇文章: https://tonybai.com/2017/06/23/an-intro-about-goroutine-scheduler/ 如果想从源码的角度来了解实现,可以看下《 Go 语言学习笔记》的源码剖析,虽然是基于 1.5 版本,但是调度器方面在以后的的版本中更新不多,所以完全没问题 |
8
mikulch 2019-11-01 09:27:52 +08:00
go 语言的包管理器现在完善的如何?
|
10
xmge 2019-11-01 09:54:51 +08:00 1
go 的协成是一种轻量级的线程,一个线程通过 go 的调度动态地与多个协程绑定。
|
11
lolizeppelin 2019-11-01 10:07:07 +08:00 8
线程
一个线程跑满,就吃饱一个 cpu N 个线程跑满对应 N 个 cpu,只要与 cpu 数量相同多线程就能吃饱 cpu 但是实际代码里会阻塞,一阻塞会自动让出 cpu, 所以在阻塞写法里,程序的线程超过 cpu 数量能提高性能 但是过多的线程数带来的上下文切换回拖慢整体 协程 通过寄存器保存代码片段,遇到阻塞(一般是 io,或者自定义的协程锁),会切换到其他代码片段 所有代码片段的入口存放一个不停排序的队列 一个循环(相当于一个线程)不停的排序这个队列,并弹出最前面的数据获取到代码片段并执行 一般只有一个主线程,由于代码片段的切换由程序自己决定,没有系统级上下文切换,性能好,缺点是单线程 所以性能最好的方式是结合多线程与协程,但是非语言级很难用一个库来实现支持多线程的协程 目前除了 go, dart (也就是 google 的 flutter 所用语言)也有多线程协程支持 我瞎鸡巴说的...我没写过 go 哈哈哈哈哈哈 |
12
youxiachai 2019-11-01 10:11:10 +08:00 1
有兴趣的其实去可以了解一下
CSP ( Communicating Sequential Process )理论 |
13
richzhu 2019-11-01 10:30:30 +08:00
@lolizeppelin 卧槽 大佬,你这段话我要加入到面试复习笔记里
|
14
ZSeptember 2019-11-01 11:00:44 +08:00 2
goroutine 的底层也是线程池,G 相当于一个 Runable。
goroutine 的优化是将阻塞都放在用户态,自己调度,就不会创建很多的系统线程了。 比如,将所有的 IO 操作都封装了,底层使用 epoll 之类的非阻塞接口,对外暴露同步阻塞接口,所以不会真正阻塞底层线程。 同步,锁之类的都是在用户态 |
15
CosimoZi 2019-11-01 11:35:54 +08:00 via Android 2
协程的本质就是 continuation
|
16
godoway 2019-11-01 12:37:17 +08:00 via Android 1
不如问一下,go 的协程和 kt 的协程有什么区别。
个人认为 kt 的用起来更简单 |
17
zunceng 2019-11-01 13:37:29 +08:00 1
大部分的 thread pool 都不用实现了 , 在一定程度上协程随便开。
Ps:一定程度上 ,我没试过开上万个 goroutine, 超过一定程度我估计也会吃不消 |
18
reus 2019-11-01 13:54:26 +08:00
语义上没有区别。
|
19
lolizeppelin 2019-11-01 14:41:24 +08:00 1
|
20
lolizeppelin 2019-11-01 14:43:07 +08:00
话说...好像这些都是从 erlang 里出来的?
|
21
JohnSmith 2019-11-01 16:13:13 +08:00
协程 vs 线程
|
22
optional 2019-11-01 18:19:21 +08:00 2
@lhx2008 『用户态调度,是不是可以抽象理解为把阻塞代码前后进行分割,分成小的代码块,放入线程池中执行呢』
原理上是这样没错,但是有个重要的区别: 协程:主动放弃 cpu 线程:抢占式调度 所以,对于 io 型应用来说,前者少了很多后者『抢到了 cpu,发现数据还没准备好,又放弃 cpu 』的消耗。 |
23
Les1ie 2019-11-01 18:26:58 +08:00 1
楼上的一个线程跑满吃掉一个 CPU,多个线程多个 CPU 似乎是没分清线程和进程的区别
如果我没理解错的话,进程一次只能在一个 CPU 上,进程可以调度到其他 CPU 上,但是一次只会在 1 个地方 而线程,是运行在进程的上下文中的。 |
24
lhx2008 OP @optional #22
抢占式调度怎么理解呢,通常来说,支持抢占式不是更好吗? Go 不是也有说要支持抢占式调度的协程。 您说的 『抢到了 cpu,发现数据还没准备好,又放弃 cpu 』 是在有锁的情况下才会争抢吧?而且像 Java 的 AQS,抢不到锁也是直接排队等待解锁了。争抢的情况似乎也不是很明显。 我的理解只是这样调度的粒度更小,所以效率提高了。 |
25
lhx2008 OP @optional #22
如果是说线程和 CPU 数量相同,可以减少线程之间被 CPU 核切换。那其实线程池也是可以配置的。 |
26
vkhsyj 2019-11-01 20:28:28 +08:00 1
线程池还是靠 OS 来调度线程,go 的携程是语言内置的调度器来调度,当然还是受操作系统调度(
|
27
optional 2019-11-01 20:31:11 +08:00 2
@Les1ie 『线程是 cpu 调度的基本单位』。
@lhx2008 协程理论上不需要抢占式调度,而且理论上协程不可中断(没有 handle 指向它),除非你把他的执行线程干掉,至于为什么搞出个抢占式的,我只能这么理解『为了防止某个协程一直不主动释放,导致其它饿死』,实现也是怪怪的,做的太多就变成"用户态线程了"。 至于为什么说协程调度效率高:对于线程调度来说,就是给自己打个 runnable 的标记,然后等调度器赏光,但是对于调度器大爷来说,它不理解你的任务细节,每次给你 300 个时间单位,你可能 10 个单位时间就干完了,剩下的 290 只能还回去(对于 IO 密集型应用这很常见),但是对于协程来说,我干完了可以把这机会给兄弟们用,直到用完这 300 个单位。 线程池没有调用栈,做不到协程的效果。 |
29
secondwtq 2019-11-01 21:13:48 +08:00 1
@lhx2008 "粒度更小"一般对应的是”更加灵活“与”效率更低“
当然不是说一刀切效率就一定更高,一刀切效果好的前提是切的地方准确 协作式多任务就正好满足这一条件,进程在明确自己不需要 CPU 时放弃 CPU,而在做事情时操作系统不来烦你,实际就保证了 CPU 一直都在做有用的事情 可以类比在你写代码时没事总是来催你的产品,没事总是来找你”支持“的队友 缺点是如果你完成了任务一直不汇报,那整个项目就 block 在你这了 我认为现代编程语言(好吧 ... 强行把 Go 称为现代编程语言也过得去)对这个问题提供了很好的抽象,所以现在才有这么多人觉得这么好用。这在 Windows 3.1 和 MacOS Classic 那个年代大概是难以想象的吧 |
30
qiyuey 2019-11-01 21:44:51 +08:00 1
|
31
halo117 2019-11-02 02:50:13 +08:00 via iPhone 1
@optional 如果只是协作式确实有阻塞其他协程饥饿问题,但 goroutine 调度目前是有 timeout 检测一旦阻塞过久会尝试主动让出执行权,只是让出时机是在特定情况下,并不全面。所以现在看 goroutine 有点半(伪)抢占的意思。后面版本据说会更进一步做成抢占式,不过这样竞态问题会更频繁?
|
32
lhx2008 OP @halo117 现在的意思好像是,死循环,长时间阻塞到内核态这种情况,就会把执行这个协程的线程扔出调度器?相对于是自动分开一个快的调度线程池和一个慢速线程池。
|
33
reus 2019-11-02 10:57:08 +08:00 via Android 1
1.14 版本将会加入基于信号的抢占调度,所以 goroutine 从来就不是协程,它的定义就是并发执行的单元,只不过早期的实现是协作式的。在加入函数入口处的抢占检查之后就已经是半协作半抢占调度了,现在要加入完备的抢占调度了。goroutine 的语义也一直和线程接近而不是协程。什么时候调度,程序员没法判断,并发访问内存也需要上锁,就是当作线程来用的,就是一种用户态线程的实现。
|