高级事件循环

优质
小牛编辑
142浏览
2023-12-01

libuv提供了非常多的控制event-loop的方法,你能通过使用多loop来实现很多有趣的功能。你还可以将libuv的event loop嵌入到其它基于event-loop的库中。比如,想象着一个基于Qt的UI,然后Qt的event-loop是由libuv驱动的,做着加强级的系统任务。

Stopping an event loop

uv_stop()用来终止event loop。loop会停止的最早时间点是在下次循环的时候,或者稍晚些的时候。这也就意味着在本次循环中已经准备被处理的事件,依然会被处理,uv_stop不会起到作用。当uv_stop被调用,在当前的循环中,loop不会被IO操作阻塞。上面这些说得有点玄乎,还是让我们看下uv_run()的代码:

src/unix/core.c - uv_run

  1. int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  2. int timeout;
  3. int r;
  4. int ran_pending;
  5. r = uv__loop_alive(loop);
  6. if (!r)
  7. uv__update_time(loop);
  8. while (r != 0 && loop->stop_flag == 0) {
  9. uv__update_time(loop);
  10. uv__run_timers(loop);
  11. ran_pending = uv__run_pending(loop);
  12. uv__run_idle(loop);
  13. uv__run_prepare(loop);
  14. timeout = 0;
  15. if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
  16. timeout = uv_backend_timeout(loop);
  17. uv__io_poll(loop, timeout);

stop_flaguv_stop设置。现在所有的libuv回调函数都是在一次loop循环中被调用的,因此调用uv_stop并不能中止本次循环。首先,libuv会更新定时器,然后运行接下来的定时器,空转和准备回调,调用任何准备好的IO回调函数。如果你在它们之间的任何一个时间里,调用uv_stop()stop_flag会被设置为1。这会导致uv_backend_timeout()返回0,这也就是为什么loop不会阻塞在I/O上。从另外的角度来说,你在任何一个检查handler中调用uv_stop,此时I/O已经完成,所以也没有影响。

在已经得到结果,或是发生错误的时候,uv_stop()可以用来关闭一个loop,而且不需要保证handler停止的顺序。

下面是一个简单的例子,它演示了loop的停止,以及当前的循环依旧在执行。

uvstop/main.c

  1. #include <stdio.h>
  2. #include <uv.h>
  3. int64_t counter = 0;
  4. void idle_cb(uv_idle_t *handle) {
  5. printf("Idle callbackn");
  6. counter++;
  7. if (counter >= 5) {
  8. uv_stop(uv_default_loop());
  9. printf("uv_stop() calledn");
  10. }
  11. }
  12. void prep_cb(uv_prepare_t *handle) {
  13. printf("Prep callbackn");
  14. }
  15. int main() {
  16. uv_idle_t idler;
  17. uv_prepare_t prep;
  18. uv_idle_init(uv_default_loop(), &idler);
  19. uv_idle_start(&idler, idle_cb);
  20. uv_prepare_init(uv_default_loop(), &prep);
  21. uv_prepare_start(&prep, prep_cb);
  22. uv_run(uv_default_loop(), UV_RUN_DEFAULT);
  23. return 0;
  24. }