今天有个面试官和我讲 go 的协程比系统的线程更慢,这个我不能理解
我不知道他的回答和我的回答哪个是有依据的,麻烦有大佬知道的,指正我一下,仅是探讨技术对错问题,谢谢。
他一上来问我 go 的协程能否做到线程不能做到的事,而且至少重复问了我 3 次。我回:总的来说是可以加快程序的运行效率。他就讲出了他的理论和依据,既然 go 协程是要由线程去接管运行的,资源也是从线程分来的,那么何谈加快运行效率,你原本线程要做的事还是没变,而且还多了管理协程的开销。后来他又提了一些问题试图来让我相信他这个理论和依据,不知道其中某个问题的时候,我回的是:不耗费资源的操作时,协程要更快,在耗费资源较多时,还是线程更快。然后他还是在反复和我纠结这个问题。在我看来 go 的协程实现是分割原本线程的资源,做到更轻量化和更灵活的资源调度。调用完资源空闲了就可以及时 gc ,就可以用更少的资源去做更多的事。到最后,他才说,我的大前提是,要做的事是非常耗费资源的操作,就感觉很搞不懂。
虽然我面试问题回答的很差,但是我依旧想知道这个问题,不知道有没有大佬来和我指正一下,
另外他还有第二个问题,既然协程这套理论这么牛逼,那么 c++ 为什么没有呢?(在我印象里 c++只有线程)
C++有协程啊,谁说没有的。。。
线程有上下文切换开销,协程没有(或者更小?)
线程占用内存大,协程占用内存更小,可以使用更多协程
协程更多是为了异步/减少阻塞吧(不知道对不对哈)
为了减少阻塞,从 callback function -> async/await -> 协程
当遇到阻塞的时候,协程可以由协程调度器调度到其他协程,并且上下文切换的开销小。
如果换作线程,阻塞就浪费 cpu 了。
c++有协程了
哦?是有的吗?我不是 c++开发者,仅有的一点对 c++的认知可以理解成是跑个 hello world 的级别。
在我看来 go 的协程实现是分割原本线程的资源,做到更轻量化和更灵活的资源调度
没什么问题。更具体地说,很重要的一个原因是 userspace thread 完全省略了操作系统调度线程和内核态切换的开销。
有一个类似的例子:为什么 C 语言里用 malloc() 分配内存,而不是直接调操作系统提供的 sbrk()?你让面试官想去吧。
他还有第二个问题,既然协程这套理论这么牛逼,那么 c++ 为什么没有呢
没更新过知识的愚昧认知。当今几乎所有现代语言里都有「协程」,只是具体含义和实现不同。我随便抓一把:Python 、JS/TS 、C++、Rust 、C#、Java/Kotlin 、Lua 、Dart…
假设你有一件事的情况下,协程的管理消耗时间远远大于执行时间,线程能比协程更快。但是你有很多件事的情况下,协程能省下很多上下文切换的开销,这部分远远大于协程的管理时间
goroutinue 的上下文切换是比线程要轻量的,还有一个 goroutinue 初始化只需要才 2K 的内存,一个线程就要 8M ,随便并发一下都比线程的并发多很多
这人技术和处事都有问题,要是入职后是你直接上级或者同组的话,这个岗可以直接 PASS
一个协程不会比一个线程更快,但是一万个协程很可能会比一万个线程更快。因为线程本身就是一种资源。你们讨论的“消耗资源”太含糊了。争执前先定义清楚。
还有个老生常谈的称呼问题:
协程 = 有栈协程/虚拟线程/用户态线程。这是在说 Go 的 goroutinue 、Java 的 Virtual Thread ;
协程 = 无栈协程/暂停之后能恢复的函数。这是在说 Python/Dart 的 Iterator 、Lua/C++20 的 Coroutines 、Rust 的 Future 状态机;
协程 = 可以指包装了一层 Dispatcher 的普通线程。这是在说 Kotlin 的 Coroutine + NewThreadDispatcher 。
协程不是为了提高任何效率, 而是为了将异步编程简化成同步化, 1:N 有栈协程好几十年了, unix 和 windows 都有专门的 api, 不是什么新概念, 无栈协程是最近些年编译器进步兴起的
别人直接就把我 pass 了,他是一面,他觉得我回答的技术不太行。
那么何谈加快运行效率,你原本线程要做的事还是没变,而且还多了管理协程的开销
go 的协程比系统的线程更慢
老哥你是真分不清这两种表述吗…
一个 CPU 密集的任务,机器不切换协程要跑 100 秒,管理协程花 1 秒,因此 101 > 100 协程更慢
要搞清楚一个事,每一个问题,都是有其前提条件的。比如,面试官说线程比协程快,如果前提条件是并发数很低的情况(并发数比 CPU 核心数少),那么是对的。你认同的观点,同样也是有前提条件的。讨论一个问题前,要先划好场景,不然就是没完没了,毫无意义的扯皮。这家公司 pass 吧
C++的协程都已经出了好几年了
我上星期才发了个帖子,用协程写 Demo 程序: /t/1117106
协程运行在线程内,怎么可能比线程更快?越是 CPU 密集型场景,协程越无用。线程跟 CPU 核心 1:1 绑定,设定好亲和性,才是最快的,多 CPU 时还要考虑 NUMA 尽量访问本地内存。
协程搞到冒烟,也不可能比手搓的多线程实现更快,只不过多线程实现复杂度较高。
协程就是,一个线程里调度运行的函数。简单讲,当去做 io 读取操作,其实很多时候是内核在读取,用户态这时候没必要等着内核读取完,把这段时间给其他函数运行,等内核读取完后继续运行。
最常见场景,爬虫,每个请求中间大量是等待 io 读取网络时间,这时候用协程就好。 还有一个 ui 常见例子,点击读取 button ,去读一个大文件并显示当 list 里,以前为了不卡死 ui 要不异步去读,要开线程,都会回调满天飞。协程就好,ui 线程读取大文件,等待过程还继续处理 ui 事件,然后读取完,继续显示列表,甚至代码逻辑就在一起。
线程是要有一套自己资源,开一个线程是废资源,其实现在配置无所谓,只不过多线程代码很多问题。但是如果代码都非 io 操作那就只能多开线程,这时候协程就没用了。
1 、goroutine 初始栈 2KB (会动态增长的,并不是说一定省内存了),而操作系统线程的栈通常 1MB
2 、Go 运行时内置调度器,相比线程由操作系统调度,goroutines 的上下文切换成本更低,避免内核态的开销。
3 、配合通道减少了竞争的问题,使用简单
缺点也有:
1 、没法控制怎么分配到 cpu 核上,开几个协程可能都挂一个线程上,,利用不了多核资源
2 、GUI 生态多是线程绑定,go 对这块的兼容很不好,没什么生态
3 、协程一旦执行阻塞系统调用,会把整个线程阻塞,导致该线程无法执行其他 goroutines ,降低并发效率
4 、协程不适合 CPU 密集型任务,因为没什么 io ,上下文切换反而增加了开销,,调度器也有损耗,不如用多线程直接绑定到核心上
“耗费资源”这个概念在你们交谈里面变得很模糊,欠缺定义。
楼主经验少点,工作几年了哦?可以刷些 Go 实现、Linux 调度的文章,了解下进程这块相关的知识。
面试官应该到最后解释一下的。
Go 的协程就是用户(态)自己管理的代码片段嘛,那资源的分配上相比 OS 提供的线程,肯定是能够自己 DIY 啦
至于 CPP ,那 CPP 人家是写 OS 的语言,我拿 CPP 写个 Linux 再写个 Golang 编译器,你说我 CPP 有没有协程?
draven.co/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/
samwho.dev/memory-allocation/
www.luozhiyun.com/archives/518
draven.co/golang/docs/part2-foundation/ch04-basic/golang-function-call/
你 CPU 跑满那肯定是系统线程快啊,但是你 CPU 跑满的东西你用 go 写?
计算密集型肯定是更慢的,协程主要是方便让一个程序更好的写成“正确的”并发模式。
cpu 密集型用线程,io 密集型用协程
这种情况在 go 里面也是一样啊,全是计算的时候,go 也没办法打断吧。
op 哥,你碰到了一个杠精面试。pass 吧。。。
其实也没那么复杂。一般又通俗的理解就是计算机的东西越底层效率越高,协程基于线程就不可能比线程的效率高。
我面试过一个号称某大厂主任级别的技术管理,结果我们聊关于国内外( b 站,油管等)视频下载技术问题。我不太懂但是市面上有这么多盗版视频,总不能都是内部泄漏的吧,就觉得肯定有办法。他让我回去好好看看,说现在大厂都有办法杜绝任何方式扒源。。。
你们不在一个频道上啊,你应该一顿 gmp 叭啦叭啦上去糊他一脸
没有啥是协程能做线程不能做的,用户态写个协程库就等态了
1 、协程是为了提高资源利用率和减少上下文切换的开销
2 、c++也有协程,只是不是语言层面上的
#3 github.com/Tencent/libco 微信的 cpp 协程库 还有 这面试官水平太臭
水货
- goroutine 也是要线程来运行的啊,性能只会一样
- linux 线程栈空间 8M ,相关上下文切换需要保存的寄存器等资源比 g 更多
让他看新闻,c 跟 cpp 都有 coroutine
线程成本高:
- 线程堆栈占用内存大
- 线程切换成本高,是需要内核才能切换
协程成本低:
- go 的堆栈是动态的,最初只会使用很小的内存空间
- go 协程切换是用户态的,成本低
另外 go 适合 io 密集型的,原生线程适合计算密集型。
这什么大厂主任😂确实挺次的吧
我总觉得这个面试官是喜欢在网上刷语言不重要,重要的是思想的那类人🤣
其实前面不少回答已经很准确了,我就补充下面试官的想法。面试官无非是想看看你对程序运行时的理解是否足够深入。Go 算是 C 语言的加强版,像 GC 、map 、channel 这些,你用 C 写就得找库或者自己实现,但 Go 就给你包装好了。协程也是一样,你用着是协程,但底层还是在线程上跑的,只不过 Go 帮你把调度逻辑写了,一个线程上可以根据需要不断切换执行各个协程的逻辑。你自己也可以实现这个,但太麻烦,而且大多数人写不对。至于为什么必须有线程,那是因为操作系统就只支持到线程。所以面试官说的确实没问题,算是考察下操作系统吧。当然这个知识点我觉得也就是层窗户纸。
不耗费资源的操作时,协程要更快,在耗费资源较多时,还是线程更快。
有点笼统,资源的定义是啥没说清楚,不过
总的来说是可以加快程序的运行效率
这一点肯定不对,协程仅仅是增加了程序的并发度,效率不一定高。一般来说,协程和线程都是解决 IO 阻塞时 cpu 空闲问题,协程可以实现更高阻塞并发,线程虽然并发程度没有协程高,但是总体上一般认为效率比协程还是高的。这里的效率指的是,协程还需要额外的开销进行 cooperative 部分,比如把异步、回调包成类似同步的操作。
换一个说法,就对于非阻塞 IO 密集型任务,比如 CPU 上纯纯的 for-loop ,协程就毫无用处。
你回答给人感觉认识不够清晰,估计就 pass 了。或者是你跟他不匹配,觉得沟通费劲,至少你自己也觉得跟他沟通费劲,以后做同事也没意思。不如友好 byebye
主要是看 git log 。 其他基本都命令行了。 个人比较喜欢 Fork 和 jetbrains 家的 git 插件。 Fork 不支持 Linux 。 我为了 git …
网易有钱 app 前几年停服了,因为很好用,所以一直侥幸用着,觉得离线问题不大。前几天因为网易游戏密码忘了,就重置了,后面发现停服的网易有钱竟然也一起掉登录了。。伴随着的还有我…
我现在经常有一些文本或者小文件需要快速共享给局域网的场景,比如 vpn 的订阅连接,某个 json 文件 yml 配置文件,临时分享个密码或者截图等等,我现在只能通过 oned…