nginx源码分析——线程池
Syntax: thread_pool name threads=number [max_queue=number];
Default: thread_pool default threads=32 max_queue=65536;
Context: main
/******************* nginx/src/core/ngx_thread_pool.c ************************/
//创建线程池所需的基础结构
static void * ngx_thread_pool_create_conf(ngx_cycle_t *cycle)
{
ngx_thread_pool_conf_t *tcf;
//从cycle->pool指向的内存池中申请一块内存
tcf = ngx_pcalloc(cycle->pool, sizeof(ngx_thread_pool_conf_t));
if (tcf == NULL) {
return NULL;
} //先申请包含4个ngx_thread_pool_t指针类型元素的数组
//ngx_thread_pool_t结构体中保存了一个线程池相关的信息
if (ngx_array_init(&tcf->pools, cycle->pool, 4,
sizeof(ngx_thread_pool_t *))
!= NGX_OK)
{
return NULL;
} return tcf;
} //解析处理配置文件中thread_pool的配置,并将相关信息保存的ngx_thread_pool_t中
static char * ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value;
ngx_uint_t i;
ngx_thread_pool_t *tp; value = cf->args->elts; //根据thread_pool配置中的name作为线程池的唯一标识(如果重名,只有第一个有效)
//申请ngx_thread_pool_t结构保存线程池的相关信息
//由此可见,nginx支持配置多个name不同的线程池
tp = ngx_thread_pool_add(cf, &value[1]);
.......
//处理thread_pool配置行的所有元素
for (i = 2; i < cf->args->nelts; i++) {
//检查配置的线程数
if (ngx_strncmp(value[i].data, "threads=", 8) == 0) {
.......
} //检查配置的最大队列长度
if (ngx_strncmp(value[i].data, "max_queue=", 10) == 0) {
.......
}
}
......
} //判断包含多个线程池的数组中的各个线程池的配置是否正确
static char * ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf)
{
....
ngx_thread_pool_t **tpp; tpp = tcf->pools.elts;
//遍历数组中所有的线程池配置,并检查其正确性
for (i = 0; i < tcf->pools.nelts; i++) {
.....
} return NGX_CONF_OK;
}
/******************* nginx/src/core/ngx_thread_pool.c ************************/
//创建线程池所需的基础结构
static ngx_int_t
ngx_thread_pool_init_worker(ngx_cycle_t *cycle)
{
ngx_uint_t i;
ngx_thread_pool_t **tpp;
ngx_thread_pool_conf_t *tcf;
//如果不是worker或者只有一个worker就不起用线程池
if (ngx_process != NGX_PROCESS_WORKER
&& ngx_process != NGX_PROCESS_SINGLE)
{
return NGX_OK;
} //初始化任务队列
ngx_thread_pool_queue_init(&ngx_thread_pool_done); tpp = tcf->pools.elts;
for (i = 0; i < tcf->pools.nelts; i++) {
//初始化各个线程池
if (ngx_thread_pool_init(tpp[i], cycle->log, cycle->pool) != NGX_OK) {
return NGX_ERROR;
}
} return NGX_OK;
} //线程池初始化
static ngx_int_t ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool)
{
.....
//初始化任务队列
ngx_thread_pool_queue_init(&tp->queue); //创建线程锁
if (ngx_thread_mutex_create(&tp->mtx, log) != NGX_OK) {
return NGX_ERROR;
} //创建线程条件变量
if (ngx_thread_cond_create(&tp->cond, log) != NGX_OK) {
(void) ngx_thread_mutex_destroy(&tp->mtx, log);
return NGX_ERROR;
}
......
for (n = 0; n < tp->threads; n++) {
//创建线程池中的每个线程
err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp);
if (err) {
ngx_log_error(NGX_LOG_ALERT, log, err,
"pthread_create() failed");
return NGX_ERROR;
}
}
......
} //线程池中线程处理主函数
static void *ngx_thread_pool_cycle(void *data)
{
......
for ( ;; ) {
//阻塞的方式获取线程锁
if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
return NULL;
} /* the number may become negative */
tp->waiting--; //如果任务队列为空,就cond_wait阻塞等待有新任务时调用cond_signal/broadcast触发
while (tp->queue.first == NULL) {
if (ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log)
!= NGX_OK)
{
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
return NULL;
}
}
//从任务队列中获取task,并将其从队列中移除
task = tp->queue.first;
tp->queue.first = task->next; if (tp->queue.first == NULL) {
tp->queue.last = &tp->queue.first;
} if (ngx_thread_mutex_unlock(&tp->mtx, tp->log) != NGX_OK) {
return NULL;
}
......
//task的处理函数
task->handler(task->ctx, tp->log);
..... ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048); //将经过预处理的任务添加到done队列中等待调用event的回调函数继续处理
*ngx_thread_pool_done.last = task;
ngx_thread_pool_done.last = &task->next; //防止编译器优化,保证解锁操作是在上述语句执行完毕后再去执行的
ngx_memory_barrier(); ngx_unlock(&ngx_thread_pool_done_lock); (void) ngx_notify(ngx_thread_pool_handler);
}
} //处理pool_done队列上task中包含的每个event事件
static void ngx_thread_pool_handler(ngx_event_t *ev)
{
.....
ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048); //获取任务链表的头部
task = ngx_thread_pool_done.first;
ngx_thread_pool_done.first = NULL;
ngx_thread_pool_done.last = &ngx_thread_pool_done.first; ngx_memory_barrier(); ngx_unlock(&ngx_thread_pool_done_lock); while (task) {
ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
"run completion handler for task #%ui", task->id);
//遍历队列中的所有任务事件
event = &task->event;
task = task->next; event->complete = 1;
event->active = 0; //调用event对应的处理函数有针对性的进行处理
event->handler(event);
}
}
/*********************** nginx/src/os/unix/ngx_files.c **********************/
//file_cache模块的处理函数(涉及到了线程池)
static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
{
.......
#if (NGX_THREADS) if (clcf->aio == NGX_HTTP_AIO_THREADS) {
c->file.thread_task = c->thread_task;
//这里注册的函数在下面语句中的ngx_thread_read函数中被调用
c->file.thread_handler = ngx_http_cache_thread_handler;
c->file.thread_ctx = r;
//根据任务的属性,选择正确的线程池,并初始化task结构体中的各个成员
n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool); c->thread_task = c->file.thread_task;
c->reading = (n == NGX_AGAIN); return n;
}
#endif return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
} //task任务的处理函数
static ngx_int_t ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
{
.......
tp = clcf->thread_pool;
....... task->event.data = r;
//注册thread_event_handler函数,该函数在处理pool_done队列中event事件时被调用
task->event.handler = ngx_http_cache_thread_event_handler; //将任务放到线程池的任务队列中
if (ngx_thread_task_post(tp, task) != NGX_OK) {
return NGX_ERROR;
}
......
} /*********************** nginx/src/core/ngx_thread_pool.c **********************/
//添加任务到队列中
ngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)
{
//如果当前的任务正在处理就退出
if (task->event.active) {
ngx_log_error(NGX_LOG_ALERT, tp->log, 0,
"task #%ui already active", task->id);
return NGX_ERROR;
} if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
return NGX_ERROR;
} //判断当前线程池等待的任务数量与最大队列长度的关系
if (tp->waiting >= tp->max_queue) {
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log); ngx_log_error(NGX_LOG_ERR, tp->log, 0,
"thread pool \"%V\" queue overflow: %i tasks waiting",
&tp->name, tp->waiting);
return NGX_ERROR;
}
//激活任务
task->event.active = 1; task->id = ngx_thread_pool_task_id++;
task->next = NULL; //通知阻塞的线程有新事件加入,可以解除阻塞
if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) {
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
return NGX_ERROR;
} *tp->queue.last = task;
tp->queue.last = &task->next; tp->waiting++; (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log); ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
"task #%ui added to thread pool \"%V\"",
task->id, &tp->name); return NGX_OK;
}
nginx源码分析——线程池的更多相关文章
- nginx源码分析线程池详解
nginx源码分析线程池详解 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...
- Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求
Elasticsearch源码分析—线程池(十一) 转自:https://www.felayman.com/articles/2017/11/10/1510291570687.html 线程池 每个节 ...
- JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor
JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...
- JUC源码分析-线程池篇(二)FutureTask
JUC源码分析-线程池篇(二)FutureTask JDK5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果.本文从源代码角度分析下具体的实现 ...
- JUC源码分析-线程池篇(三)Timer
JUC源码分析-线程池篇(三)Timer Timer 是 java.util 包提供的一个定时任务调度器,在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次. 1. Ti ...
- JUC源码分析-线程池篇(一):ThreadPoolExecutor
JUC源码分析-线程池篇(一):ThreadPoolExecutor Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池 ...
- nginx源码分析—内存池结构ngx_pool_t及内存管理
Content 0. 序 1. 内存池结构 1.1 ngx_pool_t结构 1.2 其他相关结构 1.3 ngx_pool_t的逻辑结构 2. 内存池操作 2.1 创建内存池 2.2 销毁内存池 2 ...
- nginx源码分析——内存池
内存池的目的就是管理内存,使回收内存可以自动化一些. ngx_palloc.h /* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. * ...
- Nginx源码分析:3张图看懂启动及进程工作原理
编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」. 导读:很多工程师及架构师都希望了解及掌握高性能服务器 ...
随机推荐
- 浏览器访问php脚本通过sendmail用mail函数发送邮件
前几天做项目遇到这样的一个问题:当某一个结点下有新的文章发表的时候,以邮件的形式通知该结点下的所有用户.这就需要用到邮件发送的功能. 因为项目是php语言做的,所以最简单的方法就是使用php自带的函数 ...
- 浅析Java中synchronized与static synchronized
synchronized关键字 synchronized是进行同步处理而保证线程安全.在一个方法中,如果是方法内的私有变量,那个这个变量是线程安全的,但是类中的实例变量是可能会出现线程安全问题的,当多 ...
- Mybatis基础学习(五)—缓存
一.概述 mybatis提供查询缓存,如果缓存中有数据就不用从数据库中获取,用于减轻数据压力,提高系统性能. 一级缓存是SqlSession级别的缓存.在操作数据库时需要 ...
- Oracle to_date函数
TO_DATE格式(以时间:2007-11-02 13:45:25为例)Year: yy two digits 两位年 显示值:07yyy three di ...
- python_Tornado_web_框架_分页
如何实现web_框架_分页? -- 思考什么是xss跨站攻击? --别人把js代码提交到后台,然后从后台返回数据的时候,别人上传的js变成html中的代码, 就会插入别人的代码,带来极大的信息泄露的风 ...
- html列表问题
HTML无序列表 无序列表是一个项目的列表,此列项目运用粗体圆点(典型的小黑圆圈)进行符号. 无序列表运用 标签 Coffee Milk 浏览器闪现如下: <ul "="&q ...
- JQuery分页插件封装(源码来自百度,自己封装)
最近由于项目的需要,做了一个基于JQuery的表格分页插件封装,部分源码来源百度,经由自己封装完成. 下面是具体代码和说明,仅供参考.第一步可以先将我的HTML,CSS,JS这三部分的代码创建好后先运 ...
- 初学Java scirpt(判断、循环语句)
在编写代码时,我们经常需要为不同的判断结果来执行不同的动作以及需要反复执行同一段代码,这时我们就需要使用判断和循环语句来实现. 1.判断语句(if) 判断语句经常用的有(if......else).( ...
- 关于mpu6050的几个很好的帖子
最近在研究6050,真是很磨人啊,这个小东西还挺复杂,一个读取程序竟然需要600多行. 这几天连查资料找到了几个很好的帖子,要是以后有人看到这篇帖子,可以避免误入歧途,也可以省去很多时间. 1.阿西莫 ...
- 事件的preventDefault方法
事件有一个preventDefault()方法,该方法可以用来取消事件的默认行为.许多事件都有默认执行的关联行为.例如,如果用户在文本字段中键入一个字符,则默认行为就是在文本字段中显示该字符.由于可以 ...