想探讨下 Go 中的网络编程模式

304 天前
 fgasdzxc
我知道 Go 是通过 goroutine 来实现高并发,在使用的时候,每个 IO 操作( Accpept 、Read 、Write )使用一个 goroutine 来处理,每个 goroutine 是同步阻塞的。

我还知道 Go 的网络库底层是使用了 epoll 来监听文件描述符的,一旦文件描述符的数据就绪,就把对应的 goroutine 加入到可调度队列中。

也就是说 Go 给我们提供的是同步阻塞的编程接口,使用起来非常方便简捷。但是其底层实现了 IO 多路复用,并且通过配合 GMP 的调度机制来使得就绪的文件描述符可以得到处理。在这个角度来看,Go 的网络编程模式是同步非阻塞的模式。尽管 goroutine 由于 IO 阻塞住了,但是底层的线程并没有阻塞(换了个线程接着调度之后的 goroutine ),并且通过底层的 IO 多路复用机制,一旦文件描述符就绪,相应的 goroutine 就可以被加入调度队列。

上面这些是我对 Go 的网络编程模式的理解

但是同时,又在很多博客中看到说 Go 是异步的网络编程模式,我对异步 IO 的理解是,主线程的调用直接返回,并且异步线程处理完会调用回调函数来通知主线程它处理完了返回结果。但是 Go 中显然是没有回调函数这个东西的,这算异步吗?

认为 Go 是异步的网络编程模式的人认为,新开的 goroutine 不会阻塞当前 goroutine ,所以是异步的

我想问一下我们通常所说的异步与异步 IO 是一个东西吗?
3083 次点击
所在节点    Go 编程语言
16 条回复
zzyphp111
304 天前
新开的 goroutine 不会阻塞当前 goroutine ,因此可以认为 Go 的网络编程模式是异步的。
所以说异步与异步 I/O 在 Go 中是一个东西。而你说的异步 IO 在 go 中的表现形势又是通过 chan 等机制来实现所谓的回调,而这些都在用户态就完成了,所以可以理解是一种先进的异步 IO 模式
mcfog
304 天前
这里有四个不同的概念
non-block
concurrency
parallelism
asynchronous
nebkad
304 天前
如果你学 Rust ,那就可以很容易地明白他们为什么这么说,但你学 Go 我只能说你要多想。
(对我是来挑事儿的 XD
coyove
304 天前
实际应用上讲是一个概念,因为你不会遇到 IO 以外需要异步的场景,也正是因为 iowait 的存在,才让 N 个核处理 C1M C10M 问题变得可能。

话说比如高性能科学计算,你不可能通过在 10 核上开 100 goroutine 来变成 10x 性能。
monkeyWie
304 天前
不要纠结什么阻塞、异步之类的术语,只需要明确 golang 底层是 epoll ,自动帮你把本应该异步处理的 io 通过协程做好了调度,在代码阻塞的时候实际上是不占用 cpu 资源的,这也是为什么 golang 做网络开发又简单性能又高的原因。
Nazz
304 天前
阻塞/非阻塞指的是 IO 系统调用是否会阻塞当前线程, 同步/异步指的是任意某个操作是否会阻塞当前线程/协程
thevita
304 天前
我也觉得纠结这些意义不大,这里就说是 "golang 实现了用同步的语义(语言层面)使用 IO 多路复用( API/系统调用层面)"

如果将来 golang 的 io 实现使用 io_uring 的话还可以说: "golang 实现了用同步的语义使用异步 IO “

异步/同步 也可以统一嘛。
GeekGao
304 天前
关于“回调”: 虽然 Go 并没有显式的回调函数,但是通过 goroutines 和 channels ,我们可以实现类似的功能
当一个 goroutine 完成其任务并将结果发送到 channel 时,这就像是在 “回调” 主线程。
NessajCN
304 天前
某些其他编程语言用户是真的招黑,跟编程届原神似的
小心以后上哪儿都被人说学 xx 学的
PTLin
304 天前
其实和操作系统一样的,发起读之后将这个线程挂到某个 waitlist 中,然后从运行队列里清除这个线程然后进行主动调度。当等待的条件满足后将 waitlist 中的线程放到运行队列里等待被重新调度到。
kneo
304 天前
需要标榜自己高性能的时候就说是异步,需要强调对程序员友好就说是同步。
iseki
303 天前
@Nazz 同步/异步的区分不是阻塞当前[线协]程与否,而是是否能立刻取回结果
Nazz
303 天前
@iseki "立刻"这种表述也是有问题的
iseki
303 天前
@Nazz 确实,立刻还给了时间上的含有,也不合适…要不,“同步地返回”…得,循环了
CLMan
303 天前
同步、异步、阻塞、非阻塞,本来就没有一个精确的定义,很容易因为理解不同而鸡同鸭讲,个人博客更是造成理解冲突的重灾区(个人博客充斥着二手知识,用词并非精确)。

“异步”和“异步 IO”当然不是一回事,“异步”可以用来描述任何一段子程序的执行方式,而“异步 IO”,只能用来描述 IO 操作(读写文件和网络等)的执行方式。

按照 Unix 的 IO 模型(《 Unix 网络编程卷 1 》,6.2 IO 模型),同步和异步用于描述内核和用户空间之间数据复制的过程,而阻塞和非阻塞用于描述等待资源就绪(网络资源、文件资源等)的过程。

在某些书籍、博客中,或者非 IO 操作的上下文中,会相对抽象,将`阻塞`和`同步`都解释为等待,将`非阻塞`和`异步`都解释为立即返回,我无法评价这种解释是否正确,但至少在 Unix IO 模型中,这种解释是错误的。
------
回归正题,“Go 中的网络编程模式”,你的困扰无非是该用其底层实现,还是其提供的 API 风格来描述,我个人是倾向于后者,但除非 Golang 团队亲口所说,纠结用什么定语来修饰“Go 的网络编程模式”没太多意义。
lairdnote
302 天前
@CLMan 这个理解比较全面。 现在 io_uring 的发展也不错 。

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

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

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

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

© 2021 V2EX