nodejs libuv学习

读了一下libuv源代码,简单记录一些见解 https://github.com/libuv/libuv


libev就是一个基于epoll封装事件的函数库,自身不带有线程池等操作

而libuv则是在libev基础上,加上线程操作的功能,大体运作流程如下:
主线程负责利用epoll来监察事件的到来,但有事件过来,则把事件交到异步工作线程,然后注册一个文件描述符(epoll_wait就可以知道什么时候完成),等待异步线程完成工作
在异步工作线程,完成工作后,会通过pipe通知主线程,主线程则可以通过epoll_wait得知事件完成,然后执行callback函数

libuv实现了异步的文件操作和网络通信操作。对于异步的文件操作(读写),libuv是通过自身的线程池来新开线程服务文件操作请求(因为系统提供的文件操作api都是阻塞的)。
而对于网络操作(accept,read,write)等,则是利用了底层操作系统提供的非阻塞api。
libuv函数库使用起来较简单,因为libuv直接封装好 文件/网络 操作的 异步api,用户只需要往里面填写callback函数


libuv提供了封装好的api,使开发者不需要管理线程的情况下,完成异步编程。下面将简述几个重要的api

int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb)
loop 是事件队列,新注册的异步handle要往事件队列注册, async_cb是在主线程运行的任务(工作线程通过int uv_async_send()来通知)。

int uv_queue_work(uv_loop_t* loop,uv_work_t* req,uv_work_cb work_cb,
uv_after_work_cb after_work_cb) {

work_cb是在工作线程执行的任务,after_work_cb) 是工作线程结束之后,在主线程执行的任务


主线程和工作线程的通信是通过pipe来建立(int uv_async_send()封装了pipe的操作)。主线程在epoll注册了读的事件(pipe读的一端) 当工作线程往pipe写 (通过int uv_async_send()),
主线程就会收到通知(epoll_wait),然后执行async_cb)

工作线程是一开始就初始化的。libuv提供了线程池(threadpool.c), 默认初始化四个工作线程,然后等待执行任务。所以开发者可根据服务器的cpu数量来调节线程数


一个简单的线程池实现:

开一个数组来记录空闲的线程,开一个数组来记录在运行的线程,当线程执行完,从运行线程数组remove,然后添加到空闲线程数组
开一个优先队列来记录请求工作。主线程用一个for循环(结合线程的条件变量)来询问是否有空闲的线程。当有空闲线程,便从优先队列拿优先等级最高的请求工作,把它绑定到空闲线程然后执行,并把空闲线程转移到运行线程数组


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页