异步编程的宗旨:是高效地解决并发问题,获得更高的并发请求
异步编程具体实现方案
1、事件轮询
2、回调
我们之前的文章介绍了基于线程的异步编程。今天,我们看看异步编程的真正非阻塞性质,以及如何使用 IO 中断作为事件来构建能够承受巨大工作负载的系统。
使用基于事件的范式,我们确保我们的应用程序不包含阻塞代码。在启动像数据库调用这样的 IO 操作时,线程会切换到其他任务。然后在 IO 操作完成时通知/中断该线程,因此它可以处理结果。从调用线程的角度来看,整个 IO 阶段都外包给了其他人,当工作完成时,他会发送通知。
线程池和这个模型之间的一个关键区别是,基于线程池的模型假装没有阻塞,但实际上它的块只是被推送到其他线程,而这个模型根本没有阻塞代码。调用线程的另一端不需要有线程池来接收和通知调用者。然而,问题是在 IO 操作完成后谁发出中断。
使用此特性,应用程序可以专注于触发 IO 并处理其结果,而不必担心管理 IO。libuv是一个最初用 C 语言编写的用于Node.js的 C 库,抽象出同样的 IO 管理。
I/O 是通过打开一个套接字并发出一个系统调用来开始通过该套接字发送和接收数据来执行的。当请求的数据被发送时,使用一个请求一个线程模型的应用程序将简单地轮询套接字以检查它何时收到结果。
操作系统中内置了一个轮询程序,可以非常有效地处理套接字的轮询。我们的应用程序将它自己的套接字注册为轮询套接字,并给它一个钩子,即回调,当入站数据流准备好处理时,当套接字接收到响应时,操作系统会使用该钩子通知应用程序。现在应用线程是空闲的,因为没有阻塞IO,所以可以用来处理非IO任务,可以实现大规模。在 libuv 库中,epoll是对这些 IO 套接字进行轮询的库。
使用 epoll 注册的所有套接字都被连续轮询,通常通过单个线程进行。有了响应数据和线程执行上下文(也由应用程序线程在移交给 epoll 时存储在这里),epoll 调用应用程序线程给它的回调。结果,应用程序线程将被中断以处理输出以从该点开始恢复其执行流程。
使用基于事件的样式,任务切换和线程中断发生在应用程序-OS 边界。这种交换就是我们所知的事件循环。
epoll 仍然要运行一个线程来轮询所有的套接字,这本质上是一个阻塞操作。这和线程池不是一回事吗(池中只有一个线程)?
这是正确的,但我们(应用程序)不再这样做。我们的应用程序完全是事件驱动的。操作系统在处理这些低级活动方面非常有效,例如轮询套接字。
盘点主流语言和服务器软件异步模型
在这边一起梳理一下市面上主流语言和软件的一些异步编程模型
nginx 是多进程 + 单进程 reactor 异步模型,网络请求事件都有 reactor 线程接管注册分发给 worker 进程去执行
redis 单线程 reactor 异步模型 也是事件分发注册异步编程
netty 单线程 reactor 异步模型
swoole 多线程 reactor + 多进程 worker
nodejs 单线程 + eventLoop
Golang 单线程 Reactor + 多线程协程
留言与评论(共有 0 条评论) “” |