Golang Go语言中的网络编程模式探讨

我知道 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 是一个东西吗?
Golang Go语言中的网络编程模式探讨


更多关于Golang Go语言中的网络编程模式探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

17 回复

新开的 goroutine 不会阻塞当前 goroutine ,因此可以认为 Go 的网络编程模式是异步的。
所以说异步与异步 I/O 在 Go 中是一个东西。而你说的异步 IO 在 go 中的表现形势又是通过 chan 等机制来实现所谓的回调,而这些都在用户态就完成了,所以可以理解是一种先进的异步 IO 模式

更多关于Golang Go语言中的网络编程模式探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这里有四个不同的概念
non-block
concurrency
parallelism
asynchronous

如果你学 Rust ,那就可以很容易地明白他们为什么这么说,但你学 Go 我只能说你要多想。
(对我是来挑事儿的 XD

实际应用上讲是一个概念,因为你不会遇到 IO 以外需要异步的场景,也正是因为 iowait 的存在,才让 N 个核处理 C1M C10M 问题变得可能。

话说比如高性能科学计算,你不可能通过在 10 核上开 100 goroutine 来变成 10x 性能。

不要纠结什么阻塞、异步之类的术语,只需要明确 golang 底层是 epoll ,自动帮你把本应该异步处理的 io 通过协程做好了调度,在代码阻塞的时候实际上是不占用 cpu 资源的,这也是为什么 golang 做网络开发又简单性能又高的原因。

阻塞/非阻塞指的是 IO 系统调用是否会阻塞当前线程, 同步/异步指的是任意某个操作是否会阻塞当前线程/协程

我也觉得纠结这些意义不大,这里就说是 "golang 实现了用同步的语义(语言层面)使用 IO 多路复用( API/系统调用层面)"

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

异步/同步 也可以统一嘛。

关于“回调”: 虽然 Go 并没有显式的回调函数,但是通过 goroutines 和 channels ,我们可以实现类似的功能
当一个 goroutine 完成其任务并将结果发送到 channel 时,这就像是在 “回调” 主线程。

某些其他编程语言用户是真的招黑,跟编程届原神似的
小心以后上哪儿都被人说学 xx 学的

其实和操作系统一样的,发起读之后将这个线程挂到某个 waitlist 中,然后从运行队列里清除这个线程然后进行主动调度。当等待的条件满足后将 waitlist 中的线程放到运行队列里等待被重新调度到。

需要标榜自己高性能的时候就说是异步,需要强调对程序员友好就说是同步。

同步/异步的区分不是阻塞当前[线协]程与否,而是是否能立刻取回结果

"立刻"这种表述也是有问题的

确实,立刻还给了时间上的含有,也不合适…要不,“同步地返回”…得,循环了

同步、异步、阻塞、非阻塞,本来就没有一个精确的定义,很容易因为理解不同而鸡同鸭讲,个人博客更是造成理解冲突的重灾区(个人博客充斥着二手知识,用词并非精确)。

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

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

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

这个理解比较全面。 现在 io_uring 的发展也不错 。

在Go语言中,网络编程模式展现出了极高的灵活性和效率,主要得益于其内置的net包、net/http包以及强大的并发模型。

Go语言的net包提供了一套高层网络API,涵盖了TCP、UDP等多种协议,以及DNS解析等功能。TCP协议提供可靠的、面向连接的通信,适用于需要确保数据传输完整性的场景;而UDP协议则提供无连接的快速数据传输,适用于实时性要求较高但对数据完整性要求不高的场景。

Go语言的并发模型基于轻量级的goroutines和channels。每个网络请求都可以在自己的goroutine中处理,实现高效的并发。这种并发模型不仅提高了程序的响应速度,还显著降低了系统资源的占用。

此外,Go语言的net/http包实现了HTTP协议相关的网络编程功能。通过该包,开发者可以轻松地创建HTTP服务器和客户端,进行数据的发送和接收。HTTP/2协议在Go语言中也得到了很好的支持,其引入的二进制分帧、多路复用和头部压缩等特性,进一步提升了网络传输的效率和性能。

在实际应用中,Go语言的网络编程模式展现出了广泛的应用前景。无论是构建高性能的Web服务,还是实现实时性要求较高的网络通信,Go语言都能够提供优雅且高效的解决方案。

总的来说,Go语言在网络编程方面表现出了卓越的性能和灵活性。其内置的net包和net/http包提供了丰富的网络编程功能,而基于goroutines和channels的并发模型则进一步提升了程序的并发处理能力和响应速度。这些特性使得Go语言成为网络编程领域的佼佼者。

回到顶部