nodejs ref操作_nodejs事件循环阶段之prepare

prepare是nodejs事件循环中的其中一个阶段(phase)。属于比较简单的一个阶段。我们知道libuv中分为handle和request。而prepare阶段的任务是属于handle。我们看一下他的定义。

1c40d6e066b6e304d802e5d17933208a.png

下面我们看看怎么使用它

void prep_cb(uv_prepare_t *handle) {
    printf("Prep callbackn");
}

int main() {
    uv_prepare_t prep;
    // uv_default_loop是libuv事件循环的核心结构体
    uv_prepare_init(uv_default_loop(), &prep);
    uv_prepare_start(&prep, prep_cb);
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    return 0;
}

执行main函数,libuv就会在prepare阶段执行回调prep_cb。我们分析一下这个过程。

int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* handle) {              
    uv__handle_init(loop, (uv_handle_t*)handle, UV_PREPARE);                   
    handle->prepare_cb = NULL;                                                 
    return 0;                                                                 
  } 

1 uv__handle_init是初始化libuv中handle的一个通用函数,他主要做了下面几个事情。

1 初始化handle的类型,所属loop
2 打上UV_HANDLE_REF,该标记影响事件循环的退出和poll io阶段超时时间的计算。具体在start函数的时候分析。
3 handle插入loop->handle_queue队列的队尾,每个handle在init的时候都会插入libuv的handle队列。

2 初始化prepare节点的回调。
init函数主要是做一些初始化操作。我们继续要看start函数。

 int uv_prepare_start(uv_prepare_t* handle, uv_prepare_cb cb) {           

       // 如果已经执行过start函数则直接返回
    if (uv__is_active(handle)) return 0;                                      
    if (cb == NULL) return UV_EINVAL;                                         
    QUEUE_INSERT_HEAD(&handle->loop->prepare_handles, &handle->queue);         
    handle->prepare_cb = cb;                                                   
    uv__handle_start(handle);                                                 
    return 0;                                                                 
  }   

1 设置回调,把handle插入loop中的prepare_handles队列,prepare_handles保存prepare阶段的任务。在事件循环的prepare阶段会逐个执行里面的节点的回调。

2 设置UV_HANDLE_ACTIVE标记位,如果这handle还打了UV_HANDLE_REF标记(在init阶段设置的),则事件循环中的活handle数加一。UV_HANDLE_ACTIVE标记这个handle是活的,影响事件循环的退出和poll io阶段超时时间的计算。有活的handle的话,libuv如果运行在默认模式下,则不会退出,如果是其他模式,会退出。
执行完start函数,libuv的结构体大概如下。

33fffe17d319313483a1d25716dc4765.png

然后我们看看libuv在事件循环的prepare阶段是如何处理的。

 void uv__run_prepare(uv_loop_t* loop) {                                      
    uv_prepare_t* h;                                                         
    QUEUE queue;                                                              
    QUEUE* q;                                                                 

    /*
        把该类型对应的队列中所有节点摘下来挂载到queue变量,
        相当于清空prepare_handles队列,因为如果直接遍历prepare_handles队列,
        在执行回调的时候一直往prepare_handles队列加节点,会导致下面的while循环无法退出。
        先移除的话,新插入的节点在下一轮事件循环才会被处理。
    */                            
     QUEUE_MOVE(&loop->prepare_handles, &queue);    
   // 遍历队列,执行每个节点里面的函数
    while (!QUEUE_EMPTY(&queue)) {                                            

      // 取下当前待处理的节点,即队列的头
      q = QUEUE_HEAD(&queue);                                                 

      // 取得该节点对应的整个结构体的基地址,即通过结构体成员取得结构体首地址
      h = QUEUE_DATA(q, uv_prepare_t, queue);                                

      // 把该节点移出当前队列
      QUEUE_REMOVE(q);                                                        

     // 重新插入原来的队列
      QUEUE_INSERT_TAIL(&loop->prepare_handles, q);                            

     // 执行回调函数
      h->prepare_cb(h);                                                        
    }                                                                         
  } 

run函数的逻辑很明了,就是逐个执行prepare_handles队列的节点。我们回顾一开始的测试代码。因为他设置了libuv的运行模式是默认模式。又因为有或者的handle(prepare节点),所以他是不会退出的。他会一直执行回调。那如果我们要退出怎么办呢?或者说不要执行prepare队列的某个节点了。我们只需要stop一下就可以了。

 int uv_prepare_stop(uv_prepare_t* handle) {                               
    if (!uv__is_active(handle)) return 0;                                     

    // 把handle从prepare队列中移除,但是还挂载到handle_queue中
    QUEUE_REMOVE(&handle->queue);                                             

   // 清除active标记位并且减去loop中handle的active数
    uv__handle_stop(handle);                                                  
    return 0;                                                                 
  } 

stop函数和start函数是相反的作用,就不分析了。这就是nodejs中prepare阶段的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值