ngx_thread_pool_init()

文章详细介绍了Nginx中ngx_thread_pool_cycle函数的工作流程,包括从任务队列获取任务,处理后放到完成队列,并使用ngx_notify通知主线程。同时,解释了pthread_cond_wait函数在多线程同步中的作用,如何配合pthread_mutex进行等待和唤醒操作。文中还探讨了线程池在处理I/O密集型任务,尤其是涉及磁盘操作时的性能优化,以及在不同操作系统环境下,如FreeBSD的异步读操作接口。

 

 

ngx_thread_pool_cycle()函数的主要工作是从待处理的任务队列中获取一个任务,然后调用任务对象的handler()函数处理任务,完成后把任务放置到完成队列中,并通过ngx_notify()通知主线程

 

 

 

 

 

手写线程池与性能分析 - 知乎 

 

 

 

pthread_cond_wait函数的原理

  1. pthread_cond_wait()
    用于阻塞当前线程,等待别的线程使用pthread_cond_signal()pthread_cond_broadcast来唤醒它 pthread_cond_wait() 必须与pthread_mutex配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex
  2. pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
    使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次
  3. 但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续
    wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程. 另外,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐对pthread_cond_wait()
    使用while循环来做条件判断.

 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex).

 

/******************* 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;

 

大多数读写文件操作都需要通过缓慢的磁盘。如果有充足的内存来存储数据,那么操作系统会缓存频繁使用的文件,也就是“页面缓存”(page cache)机制。

由于页面缓存机制,nginx几乎在所有情况下都能体现非常好的性能。通过页面缓存读取数据非常快,并且不会阻塞。另一方面,卸下任务到任务池是有瓶颈的。所以在内存充足并且使用的数据不是非常大的时候,nginx即使不使用线程池也是几乎工作在最佳状态。

卸下写操作到任务池中,是一个适用于特殊场景的处理方案,适用于大量无法使用VM缓存的请求操作。
例如一个高负荷的基于Nginx的视频流服务器。另外FreeBSD的用户不需要担心这些,因为FreeBSD已经有很好的异步读操作接口,无需使用线程池

ngx_thread_pool_t结构体 struct ngx_thread_pool_s { ngx_thread_mutex_t mtx; ngx_thread_pool_queue_t queue; ngx_int_t waiting; ngx_thread_cond_t cond; ngx_log_t *log; ngx_str_t name; ngx_uint_t threads; ngx_int_t max_queue; u_char *file; ngx_uint_t line; };

static ngx_int_t ngx_http_flv_live_init(ngx_conf_t *cf) { ngx_str_t *value; ngx_array_t a; ngx_http_handler_pt *h; ngx_http_flv_live_fmt_t *fmt; ngx_http_flv_live_main_conf_t *lmcf; ngx_http_core_main_conf_t *cmcf; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_flv_live_module); if (lmcf->combined_used) { if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { return NGX_ERROR; } value = ngx_array_push(&a); if (value == NULL) { return NGX_ERROR; } *value = ngx_http_combined_fmt; fmt = lmcf->formats.elts; if (ngx_http_flv_live_compile_format(cf, NULL, fmt->ops, &a, 0) != NGX_CONF_OK) { return NGX_ERROR; } } cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_flv_live_handler; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_flv_parser_body_filter; return NGX_OK; } static ngx_http_module_t ngx_http_flv_live_module_ctx = { NULL, /* preconfiguration */ ngx_http_flv_live_init, /* postconfiguration */ ngx_http_flv_live_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_flv_live_create_loc_conf, /* create location configuration */ ngx_http_flv_live_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_flv_live_module = { NGX_MODULE_V1, &ngx_http_flv_live_module_ctx, /* module context */ ngx_http_flv_live_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING };
最新发布
08-28
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值