专栏首页原创分享nodejs事件循环阶段之prepare

nodejs事件循环阶段之prepare

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

在这里插入图片描述

下面我们看看怎么使用它

void prep_cb(uv_prepare_t *handle) {
    printf("Prep callback\n");
}

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的结构体大概如下。

然后我们看看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阶段的过程。

本文分享自微信公众号 - 编程杂技(theanarkh),作者:theanarkh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • libuv之unix域的使用

    之前分析了unix域在libuv的基本原理。今天以一个简单的例子看一下如何使用它。本文涉及到一些网络编程的知识,不过文章不打算讲解这些,如果不了解可以先了解一下...

    theanarkh
  • nodejs事件循环阶段之close

    close是nodejs每轮事件循环中最后的一个阶段。我们看看怎么使用。我们知道对于一个handle,他的使用一般是init,start,stop。但是如果我们...

    theanarkh
  • libuv源码解析之信号处理

    经过一系列的操作后,主要是申请了一个用于互斥控制的管道,然后往管道里写数据。后面就可以使用lock和unlock进行加锁解锁。接着在第一个注册信号的时候,还会做...

    theanarkh
  • Unity Shader 玻璃效果

    一个玻璃效果主要分为两个部分,一部分是折射效果的计算,另一部分则是反射。下面分类进行讨论:

    汐夜koshio
  • 数据库MySQL-函数

    cwl_java
  • 浅析大规模生产网络的纵深防御架构

    纵深防御这个在安全行业被用的很烂的词,乙方的顾问写方案时信手捏来,我想大家的理解可能并不一致。其实我比较赞同lake2用的“河防”以及数字公司用的“塔防”的概念...

    FB客服
  • 大前端的未来是否可期?了解下历史先!

    在很久很久以前的公元1993年,位于美国的伊利诺州诞生了一位名为NCSAMosaic的孩子。它便是传说中的微软IE、网景以及后续众多网页浏览器的鼻祖。可惜在当时...

    用户1272076
  • 通过Amazon Machine Learning建立一个数值回归模型

    用户1737318
  • 重磅 | FreeBuf《2017企业安全威胁统一应对指南》完整版发布

    了解年度行业动态、把握安全威胁、应用安全应对流程,掌握这三个要素,是企业保障业务安全、赢得用户信任、维持未来稳定增长的关键。 近年来,网络安全的国际形势日益严峻...

    FB客服
  • 学而不思则罔 - SAP云平台ABAP编程环境的由来和适用场景

    最近Jerry写了一系列关于SAP云平台ABAP编程环境的技术文章,这些文章都是围绕着在云上的ABAP编程环境的具体知识点来分享,比如要完成一个具体的开发需求,...

    Jerry Wang

扫码关注云+社区

领取腾讯云代金券