libuv的事件驅動模型

libuv很是高效, 它利用事件驅動/異步IO, 實現線程的高度複用; 好比定時器, 網絡傳輸等, 均可以使用同一個線程來執行. 本文是以unix/core.c爲例.linux

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;

  r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);

  //主循環
  while (r != 0 && loop->stop_flag == 0) {
    //獲取當前系統時間, linux上調用系統的clock_gettime
	uv__update_time(loop);
	
	//檢查定時器是否達到觸發條件, 若是須要觸發, 將執行timer的回調函數
    uv__run_timers(loop);
	
	//執行其它的待處理的任務, 好比網絡事件, uv__io_poll觸發網絡事件時, 會將觸發的socket存入poll_fds
    ran_pending = uv__run_pending(loop);
	
	//uv__run_idle和uv__run_prepare是用宏定義出來的, IDE沒辦法直接找到它的定義; 
	//見loop-watcher.c中的#define UV_LOOP_WATCHER_DEFINE(name, type)
	
	//監聽idle事件和prepare事件, 它們的本質是同樣的; 若是設置了這樣的事件, 則本循環將一直循環, 不會停頓;
    uv__run_idle(loop);
    uv__run_prepare(loop);

	//計算下一次須要定時的時間; 若是設置了idle或者是prepare事件, 這個值爲0, 不然就是timer中最短的那個超時時間
    timeout = 0;
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

	//linux調用epoll來監聽網絡事件, 並利用它實現定時
    uv__io_poll(loop, timeout);
	
	//與uv__run_idle, uv__run_prepare同樣的機制, 用的少
    uv__run_check(loop);
	
	//close的相關操做也能夠是異步的.
    uv__run_closing_handles(loop);

    if (mode == UV_RUN_ONCE) {
      /* UV_RUN_ONCE implies forward progress: at least one callback must have
       * been invoked when it returns. uv__io_poll() can return without doing
       * I/O (meaning: no callbacks) when its timeout expires - which means we
       * have pending timers that satisfy the forward progress constraint.
       *
       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
       * the check.
       */
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
  }

  /* The if statement lets gcc compile it to a conditional store. Avoids
   * dirtying a cache line.
   */
  if (loop->stop_flag != 0)
    loop->stop_flag = 0;

  return r;
}

在由loop調用的各類回調函數中, 添加或刪除定時器, 或建立/銷燬新的網絡鏈接, 是安全的. 可是若是在其它的線程中, 直接調用loop的接口, 不是線程安全的, 很是危險. 因此libuv一般的例子是這樣的:安全

int main() {
    loop = uv_default_loop();
    //在loop循環以前, 先把各類定時或網絡任務設置好.
    uv_timer_init(loop, &gc_req);
    uv_unref((uv_handle_t*) &gc_req);

    uv_timer_start(&gc_req, gc, 0, 2000);

    // could actually be a TCP download or something
    uv_timer_init(loop, &fake_job_req);
    uv_timer_start(&fake_job_req, fake_job, 9000, 0);
	
	//各類定時任務初始化完成以後, 再執行loop循環
    return uv_run(loop, UV_RUN_DEFAULT);
}
相關文章
相關標籤/搜索