libuv学习笔记(20)
线程池工作调度
相关数据结构
typedef struct uv_work_s uv_work_t;
struct uv_work_s {
UV_REQ_FIELDS//uv_req_t的成员
uv_loop_t* loop;
uv_work_cb work_cb;
uv_after_work_cb after_work_cb;
//UV_WORK_PRIVATE_FIELDS展开:
struct uv__work work_req;
};
//涉及到需要线程池处理的请求都会带有这个结构体
struct uv__work {
void (*work)(struct uv__work *w);//具体的任务函数
void (*done)(struct uv__work *w, int status);//任务完成之后由loop调用的函数
struct uv_loop_s* loop;
void* wq[2];//任务队列
};
相关函数
将任务添加到线程池
int uv_queue_work(uv_loop_t* loop,
uv_work_t* req,
uv_work_cb work_cb,
uv_after_work_cb after_work_cb) {
if (work_cb == NULL)
return UV_EINVAL;
uv__req_init(loop, req, UV_WORK);//初始化req
req->loop = loop;
req->work_cb = work_cb;
req->after_work_cb = after_work_cb;
//内部处理
uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done);
return 0;
}
void uv__work_submit(uv_loop_t* loop,
struct uv__work* w,
void (*work)(struct uv__work* w),
void (*done)(struct uv__work* w, int status)) {
uv_once(&once, init_once);//初始化线程池,只会执行一次
w->loop = loop;
w->work = work;
w->done = done;
post(&w->wq);//将任务添加到线程池任务队列
}
初始化线程池
static void init_once(void) {
unsigned int i;
const char* val;
nthreads = ARRAY_SIZE(default_threads);//默认4个线程
val = getenv("UV_THREADPOOL_SIZE");//可以通过这个环境变量改变
if (val != NULL)
nthreads = atoi(val);
if (nthreads == 0)
nthreads = 1;//至少一个
if (nthreads > MAX_THREADPOOL_SIZE)
nthreads = MAX_THREADPOOL_SIZE;//最多128个
threads = default_threads;
if (nthreads > ARRAY_SIZE(default_threads)) {
threads = uv__malloc(nthreads * sizeof(threads[0]));
if (threads == NULL) {
nthreads = ARRAY_SIZE(default_threads);
threads = default_threads;
}
}
if (uv_cond_init(&cond))
abort();
if (uv_mutex_init(&mutex))//初始化互斥量,内部由临界区实现
abort();
QUEUE_INIT(&wq);//初始化全局任务队列
for (i = 0; i < nthreads; i++)
if (uv_thread_create(threads + i, worker, NULL))
abort();
initialized = 1;
}
将任务添加到队列
static void post(QUEUE* q) {
uv_mutex_lock(&mutex);//锁定
QUEUE_INSERT_TAIL(&wq, q);//添加到队列末尾
if (idle_threads > 0)//有线程空闲
uv_cond_signal(&cond);//唤醒空闲线程中的一个线程
uv_mutex_unlock(&mutex);
}
每个线程调用的函数
static UINT __stdcall uv__thread_start(void* arg) {
struct thread_ctx *ctx_p;
struct thread_ctx ctx;
ctx_p = arg;
ctx = *ctx_p;
uv__free(ctx_p);
uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
uv_key_set(&uv__current_thread_key, (void*) ctx.self);//设置线程局部变量,也就是线程句柄
ctx.entry(ctx.arg);//调用worker
return 0;
}
线程池内每个线程调用的函数
static void worker(void* arg) {
struct uv__work* w;
QUEUE* q;
(void) arg;
for (;;) {
uv_mutex_lock(&mutex);
while (QUEUE_EMPTY(&wq)) {//没有任务
idle_threads += 1;//计数加一
uv_cond_wait(&cond, &mutex);//释放临界区并一直等待
idle_threads -= 1;//被激活
}
q = QUEUE_HEAD(&wq);//取第一个
if (q == &exit_message)//是退出消息
uv_cond_signal(&cond);//唤醒线程,如此一来就能唤醒所有等待的线程
else {
QUEUE_REMOVE(q);
QUEUE_INIT(q); /* Signal uv_cancel() that the work req is
executing. */
}
uv_mutex_unlock(&mutex);
if (q == &exit_message)//堆出消息,直接结束线程
break;
w = QUEUE_DATA(q, struct uv__work, wq);//获取对应的uv__work
w->work(w);//调用任务函数,内部会调用用户的任务回调函数
//向loop发送wake消息,有loop调用done函数
uv_mutex_lock(&w->loop->wq_mutex);
w->work = NULL; /* Signal uv_cancel() that the work req is done
executing. */
QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
uv_async_send(&w->loop->wq_async);
uv_mutex_unlock(&w->loop->wq_mutex);
}
}
取消一个线程池任务
int uv_cancel(uv_req_t* req) {
struct uv__work* wreq;
uv_loop_t* loop;
switch (req->type) {
case UV_FS:
loop = ((uv_fs_t*) req)->loop;
wreq = &((uv_fs_t*) req)->work_req;
break;
case UV_GETADDRINFO:
loop = ((uv_getaddrinfo_t*) req)->loop;
wreq = &((uv_getaddrinfo_t*) req)->work_req;
break;
case UV_GETNAMEINFO:
loop = ((uv_getnameinfo_t*) req)->loop;
wreq = &((uv_getnameinfo_t*) req)->work_req;
break;
case UV_WORK:
loop = ((uv_work_t*) req)->loop;
wreq = &((uv_work_t*) req)->work_req;
break;
default:
return UV_EINVAL;
}
return uv__work_cancel(loop, req, wreq);
}
static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
int cancelled;
uv_mutex_lock(&mutex);//进入临界区
uv_mutex_lock(&w->loop->wq_mutex);
cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
if (cancelled)//还没被处理,直接从列表中去掉即可
QUEUE_REMOVE(&w->wq);
uv_mutex_unlock(&w->loop->wq_mutex);
uv_mutex_unlock(&mutex);
if (!cancelled)//正在被处理
return UV_EBUSY;
//去掉之后,直接将任务插入loop的任务列表,并发送异步请求唤醒loop,以便loop调用线程池任务的完成回调函数,走完整个流程
w->work = uv__cancelled;
uv_mutex_lock(&loop->wq_mutex);
QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
uv_async_send(&loop->wq_async);
uv_mutex_unlock(&loop->wq_mutex);
return 0;
}
注意,uv_cancel取消一个线程池任务之后,并不是立刻停止了任务,还是会继续走流程,最终还是会由loop所在线程调用任务的完成回调函数,只是完成回调函数中会指明当前任务被取消了,非正常完成。