golang 面试之协程比线程更轻量级?

2020-06-04 20:21:15 +08:00
 xmge

最近在 golang 面试,一般都会设计到为什么 go 语言更好地支持高并发,

答:在高并发场景下,创建一个协程比创建一个线程消耗的资源少很多,一般线程创建需要 8Mb,而协程只需要 2kb,所以在同性能的服务器下,协程可以支持更多的并发量。

有的面试官就会问:协程比线程为什么少占这么多资源?

h 在网上搜索相关文章,并没有找到很直接,很具体的说法,

是不是只有我一个人不知道啊,望知道的大佬给普及下,感谢....

7082 次点击
所在节点    程序员
42 条回复
gimp
2020-06-05 09:03:33 +08:00
线程运行在进程中,协程运行在线程中。
ipwx
2020-06-05 09:09:18 +08:00
因为切换线程是内核管的,要存储 /恢复一堆上下文。因为线程是通用的模型,所以操作系统内核为了不出错,会比知道更多程序运行细节的 go 编译器做更多的备份 /还原操作。另外切换线程进内核是需要中断触发的,又是一套比较复杂的流程。
ipwx
2020-06-05 09:11:07 +08:00
@hercule 肯定是内核态用户态啊。协程是 go 模拟出来的,一堆协程运行在同一个线程上,Go 因为知道更多程序运行信息,不需要做 overkill 的上下文备份 /还原,上下文开销自然就小了。而且不用过中断,不用进内核(进内核需要改 CPU 特权等级,有不少操作要做),省了太多事情。
129tyc
2020-06-05 09:17:03 +08:00
答案很简单,因为线程创建会分配更大的调用栈空间,比如 linux 下可以是 10M,而 go 协程创建默认的调用栈大小是 2K
justicelove
2020-06-05 09:25:32 +08:00
就是内核线程和用户线程的区别, java1.2 之前也是用户线程,后来改成了内核线程
BingoXuan
2020-06-05 09:26:20 +08:00
现实当中,裸机跑程序效率最高的。加入 OS 这么方便的一层后效率就低很多了。所以要高效最好不要让操作系统重新调度资源。因而在操作系统最小调度单位线程下,通过协程做并发处理好过用操作系统线程调度。如果进程是厂房,线程是流水线,那么协程流水线自动适应生产不同产品,避免闲置流水线或新建流水线来生产。让 n 条流水线生产出 m 种产品( m>n )。
ylsc633
2020-06-05 09:49:45 +08:00
你这个问题 我去某大厂面试也被问到! 一模一样的! 可惜当时我并没有看过也不记得操作系统相关

emmmm.. 现在也没怎么看,就看了一些大佬关于 gmp 的!

然后根据文章 汇总了一篇文章

http://interview.wzcu.com/Golang/goroutine.html#goroutine-%E5%92%8C-thread-%E7%9A%84%E5%8C%BA%E5%88%AB

希望对你有帮助(里面还有上百套笔试题,很多都是我面试亲自遇到收集的)! 另外, 我的想法是 还是得深入研究下 操作系统..(毕业多年,已完全忘光)
ylsc633
2020-06-05 09:51:20 +08:00
对了 记得看下这个概念 http://interview.wzcu.com/Golang/morestack.html
keshawnvan
2020-06-05 10:08:37 +08:00
1.堆栈刚开始比较小
2.比起线程去掉了线程局部存储
fiypig
2020-06-05 10:19:44 +08:00
@ylsc633 大佬可以的 ,收藏起来了
Philippa
2020-06-05 10:53:54 +08:00
大家看看第一个回答的视频,一个个技术要求来不断进化设计,看 20 分钟就好了,后面太多细节。就是那貌似是俄语口音的英语也只有自动英语字幕,不容易听。
mengzhuo
2020-06-05 11:04:55 +08:00
1. 线程没这么多,一个 Goroutine 现在是 384 字节
2. 轻量是因为,线程上下文切换不仅大,还需要 flush TBL
haha370104
2020-06-05 11:11:56 +08:00
本质上就是单线程,我的个人理解是 runtime 机制带来的程序内的控制权转交
cholerae
2020-06-05 13:14:01 +08:00
@mengzhuo 同进程的线程切换为什么要刷 tlb,而且新一些的 cpu 上有 asid 不用把 tlb 全刷掉了。
yukiloh
2020-06-05 13:44:40 +08:00
一楼一上来是个空白,吓了我一跳..
mengzhuo
2020-06-05 14:03:44 +08:00
@cholerae 搞错哈,现在不用刷的话性能应该没有什么区别了吧。
liuxingdeyu
2020-06-05 15:35:13 +08:00
在想一个问题,是不是还有个原因,就是协程使用的时间成本相对小于线程,因为协程的时间粒度更小
lff0305
2020-06-05 16:10:26 +08:00
@sagaxu 没这么大, C/C++ call createThread api 的时候可以设置栈大小, 默认好像是 1M
早几年记得 Java 好像是 512k 默认, 后来改成 128k 了 (可以用 -Xss 设置)
xsen
2020-06-05 16:33:29 +08:00
1. 协程是轻量级的线程,就是说资源占用少很多

2. 可以认为协程是用户态的轻量级线程,不会涉及到上下文切换——那自然效率更高、性能更好
这个类似用户态的网络协议栈,一样的道理

其实从模型上来说,协程的底层机制类似基于 mq 的任务分发机制;只是协程底层针对一般的 mq 任务分发,多做了一层虚拟资源的调度中心(调度逻辑 cpu 资源)
xsen
2020-06-05 16:39:11 +08:00
也就是把一个线程当成一个逻辑 cpu,然后 go 做了一个基于时间片的调度中心,最小任务是协程
因为都是在用户态(单个线程),所以资源消耗比线程小,没有上下文切换

所以性能可以更好

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

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

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

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

© 2021 V2EX