1、說明
本文會簡單介紹 libuv 的事件循環,旨在入門級别的使用,而不做深入探究,簡單來說就是,會大概用就行,先用熟練了,再去探究原理和源碼
下圖為官網的 libuv 的不同部分及其涉及的子系統的圖:

libuv 使用 handles 和 requests 來結合使用事件循環
handles 表示能夠執行某些耗時的長時間存在的對象
requests 表示短暫的操作,可以在一個 handles 上執行
下圖為官網的事件循環:
這張圖其實表明了 libuv 中的時間循環的處理過程,也就是 uv_run() 方法執行的過程,該方法内部是一個 while 循環:
- 先判斷循環是都處于活動狀态,通過判斷目前是否處于 alive 狀态,來确定事件循環是否退出;
- 運作倒計時定時器(維護所有句柄的定時器);
- 執行待執行的回調函數;
- 運作 idle 句柄;
- 運作 prepare 句柄;
- 輪詢 I/O;
- 運作 check 句柄;
- 調用 close 回調;
上述步驟中,有三個句柄被重點标出,我們就來讨論這三個句柄
2、idle句柄
idle handle 即空閑句柄,從上面流程圖上可以看出,如果啟動了 idle handle,每次事件循環的時候都會執行一遍其回調
2.1、uv_idle_init
該方法用于初始化 idle handle
int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle)
uv_idle_t 是 idle 句柄類型
該方法永遠執行成功,傳回值0
2.2、uv_idle_start
該方法用于開始 idle handle
int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb)
該方法用于都是執行成功的(傳回值0),除非回調函數設定為 NULL(此時傳回 UV_EINVAL)
回調函數聲明如下:
void (*uv_idle_cb)(uv_idle_t* handle);
回調函數會把句柄帶過去
2.3、uv_idle_stop
該方法用于停止 idle handle
int uv_idle_stop(uv_idle_t* idle)
該方法永遠執行成功,傳回值0
執行之後,回調不會再執行
3、prepare句柄
可以了解成準備句柄,從流程圖中可以看出,在 idle 之後,在輪詢 IO 之前執行其回調
其API和 idle 差不多
3.1、uv_prepare_init
int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare);
初始化句柄,uv_prepare_t 為 prepare 句柄類型
傳回值0,總是成功的
3.2、uv_prepare_start
int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb);
開始句柄,執行總是成功的(傳回0),除非回調函數為 NULL(此時傳回 UV_EINVAL )
void (*uv_prepare_cb)(uv_prepare_t* handle);
3.3、uv_prepare_stop
int uv_prepare_stop(uv_prepare_t* prepare);
停止句柄,回調函數不會再執行
4、check句柄
可以了解為檢查句柄,如果程式中啟動了 check 句柄,則在每次輪詢 IO 之後執行其回調函數,正好和 prepare 前後呼應
這種設計的機制是 libuv 為使用者預留的借口,在輪詢 IO 循環狀态前後進行準備和校驗操作
其 API 和上面兩種句柄類似
4.1、uv_check_init
int uv_check_init(uv_loop_t* loop, uv_check_t* check);
初始化句柄,uv_check_t 為 check 句柄類型
方法執行總是成功的
4.2、uv_check_start
int uv_check_start(uv_check_t* check, uv_check_cb cb);
開始句柄,回調函數可以為 NULL
方法執行總是成功的(傳回0),除非回調函數為 NULL(傳回UV_EINVAL )
void (*uv_check_cb)(uv_check_t* handle);
4.3、uv_check_stop
int uv_check_stop(uv_check_t* check);
停止句柄,回調函數不會再執行
方法執行總是成功的,傳回0
5、代碼示例
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#define MAX_NUM 3
int count = 0;
void idle_cb(uv_idle_t *handle)
{
count++;
printf("idle handle callback, count = %d\n", count);
if (count >= MAX_NUM)
{
printf("idle handle stop, count = %d\n", count);
uv_stop(uv_default_loop());
}
}
void prepare_cb(uv_prepare_t *handle)
{
printf("prepare handle callback\n");
}
void check_cb(uv_check_t *check)
{
printf("check handle callback\n");
}
int main()
{
uv_idle_t idle;
uv_prepare_t prepare;
uv_check_t check;
uv_idle_init(uv_default_loop(), &idle);
uv_idle_start(&idle, idle_cb);
uv_prepare_init(uv_default_loop(), &prepare);
uv_prepare_start(&prepare, prepare_cb);
uv_check_init(uv_default_loop(), &check);
uv_check_start(&check, check_cb);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
return 0;
}
輸出結果如下:
idle handle callback, count = 1
prepare handle callback
check handle callback
idle handle callback, count = 2
prepare handle callback
check handle callback
idle handle callback, count = 3
idle handle stop, count = 3
prepare handle callback
check handle callback
上例子中沒有 IO 相關的代碼,主要用于熟悉三種句柄回調函數的執行順序