![]() |
1
huluhulu 1 天前
C++有协程啊,谁说没有的。。。
|
2
Donahue 1 天前 ![]() 线程有上下文切换开销,协程没有(或者更小?)
线程占用内存大,协程占用内存更小,可以使用更多协程 协程更多是为了异步/减少阻塞吧(不知道对不对哈) 为了减少阻塞,从 callback function -> async/await -> 协程 当遇到阻塞的时候,协程可以由协程调度器调度到其他协程,并且上下文切换的开销小。 如果换作线程,阻塞就浪费 cpu 了。 c++有协程了 |
4
w568w 1 天前 ![]() > 在我看来 go 的协程实现是分割原本线程的资源,做到更轻量化和更灵活的资源调度
没什么问题。更具体地说,很重要的一个原因是 userspace thread 完全省略了操作系统调度线程和内核态切换的开销。 有一个类似的例子:为什么 C 语言里用 malloc() 分配内存,而不是直接调操作系统提供的 sbrk()?你让面试官想去吧。 > 他还有第二个问题,既然协程这套理论这么牛逼,那么 c++ 为什么没有呢 没更新过知识的愚昧认知。当今几乎所有现代语言里都有「协程」,只是具体含义和实现不同。我随便抓一把:Python 、JS/TS 、C++、Rust 、C#、Java/Kotlin 、Lua 、Dart… |
![]() |
5
so1n 1 天前
假设你有一件事的情况下,协程的管理消耗时间远远大于执行时间,线程能比协程更快。但是你有很多件事的情况下,协程能省下很多上下文切换的开销,这部分远远大于协程的管理时间
|
6
sardina 1 天前
goroutinue 的上下文切换是比线程要轻量的,还有一个 goroutinue 初始化只需要才 2K 的内存,一个线程就要 8M ,随便并发一下都比线程的并发多很多
|
7
strobber16 1 天前
这人技术和处事都有问题,要是入职后是你直接上级或者同组的话,这个岗可以直接 PASS
|
8
kneo 1 天前 via Android ![]() 一个协程不会比一个线程更快,但是一万个协程很可能会比一万个线程更快。因为线程本身就是一种资源。你们讨论的“消耗资源”太含糊了。争执前先定义清楚。
|
9
w568w 1 天前 ![]() @w568w 还有个老生常谈的称呼问题:
协程 = 有栈协程/虚拟线程/用户态线程。这是在说 Go 的 goroutinue 、Java 的 Virtual Thread ; 协程 = 无栈协程/暂停之后能恢复的函数。这是在说 Python/Dart 的 Iterator 、Lua/C++20 的 Coroutines 、Rust 的 Future 状态机; 协程 = 可以指包装了一层 Dispatcher 的普通线程。这是在说 Kotlin 的 Coroutine + NewThreadDispatcher 。 |
10
ly841000 1 天前
协程不是为了提高任何效率, 而是为了将异步编程简化成同步化, 1:N 有栈协程好几十年了, unix 和 windows 都有专门的 api, 不是什么新概念, 无栈协程是最近些年编译器进步兴起的
|
![]() |
11
qxdo1234 OP @strobber16 别人直接就把我 pass 了,他是一面,他觉得我回答的技术不太行。
|
![]() |
12
lance6716 1 天前 via Android
> 那么何谈加快运行效率,你原本线程要做的事还是没变,而且还多了管理协程的开销
> go 的协程比系统的线程更慢 老哥你是真分不清这两种表述吗… 一个 CPU 密集的任务,机器不切换协程要跑 100 秒,管理协程花 1 秒,因此 101 > 100 协程更慢 |
13
NotLongNil 1 天前
要搞清楚一个事,每一个问题,都是有其前提条件的。比如,面试官说线程比协程快,如果前提条件是并发数很低的情况(并发数比 CPU 核心数少),那么是对的。你认同的观点,同样也是有前提条件的。讨论一个问题前,要先划好场景,不然就是没完没了,毫无意义的扯皮。这家公司 pass 吧
|
14
cnbatch 1 天前
|
![]() |
15
sagaxu 1 天前 ![]() 协程运行在线程内,怎么可能比线程更快?越是 CPU 密集型场景,协程越无用。线程跟 CPU 核心 1:1 绑定,设定好亲和性,才是最快的,多 CPU 时还要考虑 NUMA 尽量访问本地内存。
协程搞到冒烟,也不可能比手搓的多线程实现更快,只不过多线程实现复杂度较高。 |
16
dearmymy 1 天前
协程就是,一个线程里调度运行的函数。简单讲,当去做 io 读取操作,其实很多时候是内核在读取,用户态这时候没必要等着内核读取完,把这段时间给其他函数运行,等内核读取完后继续运行。
最常见场景,爬虫,每个请求中间大量是等待 io 读取网络时间,这时候用协程就好。 还有一个 ui 常见例子,点击读取 button ,去读一个大文件并显示当 list 里,以前为了不卡死 ui 要不异步去读,要开线程,都会回调满天飞。协程就好,ui 线程读取大文件,等待过程还继续处理 ui 事件,然后读取完,继续显示列表,甚至代码逻辑就在一起。 线程是要有一套自己资源,开一个线程是废资源,其实现在配置无所谓,只不过多线程代码很多问题。但是如果代码都非 io 操作那就只能多开线程,这时候协程就没用了。 |
![]() |
17
bronyakaka 1 天前 ![]() 1 、goroutine 初始栈 2KB (会动态增长的,并不是说一定省内存了),而操作系统线程的栈通常 1MB
2 、Go 运行时内置调度器,相比线程由操作系统调度,goroutines 的上下文切换成本更低,避免内核态的开销。 3 、配合通道减少了竞争的问题,使用简单 缺点也有: 1 、没法控制怎么分配到 cpu 核上,开几个协程可能都挂一个线程上,,利用不了多核资源 2 、GUI 生态多是线程绑定,go 对这块的兼容很不好,没什么生态 3 、协程一旦执行阻塞系统调用,会把整个线程阻塞,导致该线程无法执行其他 goroutines ,降低并发效率 4 、协程不适合 CPU 密集型任务,因为没什么 io ,上下文切换反而增加了开销,,调度器也有损耗,不如用多线程直接绑定到核心上 |
![]() |
18
CEBBCAT 1 天前 ![]() “耗费资源”这个概念在你们交谈里面变得很模糊,欠缺定义。
楼主经验少点,工作几年了哦?可以刷些 Go 实现、Linux 调度的文章,了解下进程这块相关的知识。 面试官应该到最后解释一下的。 Go 的协程就是用户(态)自己管理的代码片段嘛,那资源的分配上相比 OS 提供的线程,肯定是能够自己 DIY 啦 至于 CPP ,那 CPP 人家是写 OS 的语言,我拿 CPP 写个 Linux 再写个 Golang 编译器,你说我 CPP 有没有协程? https://draven.co/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/ https://samwho.dev/memory-allocation/ https://www.luozhiyun.com/archives/518 https://draven.co/golang/docs/part2-foundation/ch04-basic/golang-function-call/ |
19
MrKrabs 1 天前
你 CPU 跑满那肯定是系统线程快啊,但是你 CPU 跑满的东西你用 go 写?
|
20
mooyo 1 天前
计算密集型肯定是更慢的,协程主要是方便让一个程序更好的写成“正确的”并发模式。
|
21
fanxinxiaozhan 1 天前 via Android ![]() cpu 密集型用线程,io 密集型用协程
|
![]() |
22
fgwmlhdkkkw 23 小时 43 分钟前 via Android
@MrKrabs 这种情况在 go 里面也是一样啊,全是计算的时候,go 也没办法打断吧。
|
23
hefish 23 小时 28 分钟前
op 哥,你碰到了一个杠精面试。pass 吧。。。
|
24
000sitereg 23 小时 0 分钟前 via Android
其实也没那么复杂。一般又通俗的理解就是计算机的东西越底层效率越高,协程基于线程就不可能比线程的效率高。
|
25
DIO 22 小时 56 分钟前
我面试过一个号称某大厂主任级别的技术管理,结果我们聊关于国内外( b 站,油管等)视频下载技术问题。我不太懂但是市面上有这么多盗版视频,总不能都是内部泄漏的吧,就觉得肯定有办法。他让我回去好好看看,说现在大厂都有办法杜绝任何方式扒源。。。
|
26
testcgd 22 小时 48 分钟前 via Android ![]() 你们不在一个频道上啊,你应该一顿 gmp 叭啦叭啦上去糊他一脸
没有啥是协程能做线程不能做的,用户态写个协程库就等态了 1 、协程是为了提高资源利用率和减少上下文切换的开销 2 、c++也有协程,只是不是语言层面上的 |
27
kingcanfish 22 小时 21 分钟前
@qxdo1234 #3 https://github.com/Tencent/libco 微信的 cpp 协程库 还有 这面试官水平太臭
|
28
Flourite 22 小时 3 分钟前
水货
1. goroutine 也是要线程来运行的啊,性能只会一样 2. linux 线程栈空间 8M ,相关上下文切换需要保存的寄存器等资源比 g 更多 3. 让他看新闻,c 跟 cpp 都有 coroutine |
29
xjzshttps 22 小时 2 分钟前
线程成本高:
* 线程堆栈占用内存大 * 线程切换成本高,是需要内核才能切换 协程成本低: * go 的堆栈是动态的,最初只会使用很小的内存空间 * go 协程切换是用户态的,成本低 另外 go 适合 io 密集型的,原生线程适合计算密集型。 |
![]() |
31
agagega 21 小时 54 分钟前 via iPhone
我总觉得这个面试官是喜欢在网上刷语言不重要,重要的是思想的那类人🤣
|
32
fds 21 小时 19 分钟前
其实前面不少回答已经很准确了,我就补充下面试官的想法。面试官无非是想看看你对程序运行时的理解是否足够深入。Go 算是 C 语言的加强版,像 GC 、map 、channel 这些,你用 C 写就得找库或者自己实现,但 Go 就给你包装好了。协程也是一样,你用着是协程,但底层还是在线程上跑的,只不过 Go 帮你把调度逻辑写了,一个线程上可以根据需要不断切换执行各个协程的逻辑。你自己也可以实现这个,但太麻烦,而且大多数人写不对。至于为什么必须有线程,那是因为操作系统就只支持到线程。所以面试官说的确实没问题,算是考察下操作系统吧。当然这个知识点我觉得也就是层窗户纸。
|
33
mayli 21 小时 17 分钟前
> 不耗费资源的操作时,协程要更快,在耗费资源较多时,还是线程更快。
有点笼统,资源的定义是啥没说清楚,不过 > 总的来说是可以加快程序的运行效率 这一点肯定不对,协程仅仅是增加了程序的并发度,效率不一定高。一般来说,协程和线程都是解决 IO 阻塞时 cpu 空闲问题,协程可以实现更高阻塞并发,线程虽然并发程度没有协程高,但是总体上一般认为效率比协程还是高的。这里的效率指的是,协程还需要额外的开销进行 cooperative 部分,比如把异步、回调包成类似同步的操作。 换一个说法,就对于非阻塞 IO 密集型任务,比如 CPU 上纯纯的 for-loop ,协程就毫无用处。 你回答给人感觉认识不够清晰,估计就 pass 了。或者是你跟他不匹配,觉得沟通费劲,至少你自己也觉得跟他沟通费劲,以后做同事也没意思。不如友好 byebye |
![]() |
34
liangzaiyi 20 小时 29 分钟前
搞好前提就方便回答了。如果是 CPU 密集型,直接线程数等于 CPU 核数行了;如果是 IO 密集型,就算是单线程跑协程也比多线程好,资源的分配都不是一个级别的,可以搞一百万个协程,你试下搞一百万个线程看炸不炸。
|
35
Cannly 20 小时 15 分钟前 via iPhone
认同 15 楼说法。面试官的问题应该分场景的。
如果一个线程的任务就是能跑满一个时间片,而不会在自己的时间片中提前结束任务,那么协程完全不必要。但是如果一个线程所分配的任务,比如只需要 1/10 时间片,那么,引入协程改造,确实能把未引入协程时给其它 cpu 的任务,以协程调度方式继续在本线程运行。这就减少了线程切换,更大限度的利用了 cpu 。 但是别忘了,操作系统调度的是线程作业,程序尽早出让某个线程 CPU 也是一种协调。尽可能霸占未必主流大部分程序 |
![]() |
36
qxmqh 20 小时 12 分钟前
太纠结技术细节,从对话能看出来,即使进入岗位,以后你的日子也不好过。
|
![]() |
37
cowcomic 20 小时 11 分钟前
如果只讨论协程和线程自身内部运行时的性能,那一定是线程更好
但线程的代价更高,占用的系统资源更多,创建线程的时间更长,线程间的资源交互更慢 所以在使用层面是需要通过应用场景来确定具体用哪个 |
38
hwdq0012 18 小时 42 分钟前
c++17 之前 Boost fiber(类似 go 的协程) , boost croutine(没有调度器的协程) 都是有栈协程, c++17 开始 msvc 先有无栈协程, cpp 20 开始 各编译器都陆续有无栈协程了
|
39
HaibaraDP 18 小时 36 分钟前
计算π值,我认为线程还是比协程快的,毕竟协程在线程的基础上套了一层。对于面试问题,应该反复和面试官沟通,确认他的问题后给出答案
|
![]() |
40
LotusChuan 18 小时 16 分钟前 ![]() 这不是技术问题,是面试套路问题。面试得看人下菜,这也是体现沟通能力的一环。
问题 1 他想听到的就是协程适合 IO 密集型的业务,线程适合 CPU 密集型/普通业务。因为搜索引擎去搜网上论坛都是这种回答,面试官看过所以拿来问了,你得和他看的八股保持一致。 问题 2 他想听到的是协程很复杂,相比于线程来说会增加代码复杂性。他这么问的原理同上,网上论坛都是这么说的。至于 C++到底有没有协程他无所谓。 如果你有信心和能力说服面试官,那么再去尝试说服,不然就借坡下驴背他想听的八股就行了。一般来说面试官是说服不了的,因为他控制你面试是否通过的权力,为什么要接受你那一套?毕竟他自己那套也不是全错。 搜索引擎搜 rust async vs thread 的结果,第一条是 Reddit ,第二条是 Stackoverflow ,完全匹配面试官思路。 https://www.reddit.com/r/rust/comments/jgpvi3/asyncawait_vs_threadsatomics_and_when_you_use_each/ https://stackoverflow.com/questions/78541829/async-thread-vs-std-thread |
41
zzhirong 18 小时 14 分钟前
两者本质上都可以抽象成,一个线程池在完成多个任务队列,那么问题来了,既然两者都差不多,然后,Go 还引入了
goroutine 抽象层,为什么 Go (可能)要高效一些。如果所有任务都是非阻塞的,那么多线程和 goroutine 在性能表现上差别可能并不明显(猜想,未验证);但在现实情况中,由于 I/O 或通信等原因,不可避免会发生阻塞。传统线程一旦阻塞,则会占用整个线程资源,而 goroutine 在阻塞时会被挂起,并在等待条件满足后重新调度,大部分时候不会需要阻塞底层线程,从而更高效地利用系统资源。也就是说,如果你很 NB ,能够做到又能尽量少阻塞线程,又能把任务完成(也就是高效利用线程池,这就是 Go 调度器做的事情),那么两者差别不会很大。 |
42
leonhao 17 小时 42 分钟前
根据实际业务测一下就知道了,嘴上说有啥用
|
43
hashakei 17 小时 34 分钟前
|
![]() |
45
sagaxu 17 小时 16 分钟前
@moudy 协程不过是多线程+运行队列调度+当前 task 的上下文保存/恢复,go 是从语言层面做的,kotlin 是从库层面做的,还有一堆人用 C/C++做了类似的事情,没有什么同步方式是协程能用,线程却用不了的。二十多年前,putty 作者写 putty 的时候就用寥寥数行代码实现了上下文的保存和切换。
|
![]() |
46
cexll 17 小时 3 分钟前
刷一下 linux 底层进场线程协程的位置就知道 内核态和用户态 协程在用户态 创建与销毁都在内核态操作的
|
47
iOCZS 16 小时 55 分钟前
首先切换效率,协程更高。
其次,为什么要切换? io 的时候,让出 CPU 资源给其他任务运行,提高运行效率。 线程爆炸的时候,线程会一直切换。协程一般会控制任务队列(线程数),让多个协程在有限个线程内切换。 如果每个核心都进行 CPU 密集运算,那效率会比进行额外协程切换的高。 |
49
leetom 15 小时 25 分钟前
我觉得不一定是他要让你相信他的理论,而是对你的回答不满意,通过提出一些不一致的看法,让你深入回答问题,看看你的基本功。
|
50
laminux29 11 小时 56 分钟前
简单来说,启动一次线程去干活,相当于从家里出发去公司干活。线程干完一件事情后,还要回家。接到新任务需要从家里再次出发。协程就没这么多事,一直呆在公司,干完一件事情后,不需要回家,继续在公司干别的。这不效率差别就体现出来了。
|