找回密码
 立即注册
查看: 196|回复: 0

你知道什么是 Eventloop吗?

[复制链接]
发表于 2023-2-15 21:18 | 显示全部楼层 |阅读模式
最近了解了一下 Eventloop 这个概念,写篇文章整理一下思路。

1、Eventloop 是什么?

我在网上看了一些资料,都比较复杂,而且大多和 JavaScript 扯上关系,对初学者不友好。
我个人理解的 Eventloop,其实就是在一个大循环里,处理各种各样的事件。只是不同的 Eventloop 机制或者库,在性能和适用场景之间有差别罢了。
一个程序,只要它需要一直工作,就会处于一个持续循环运行的状态,我把这个循环的状态,称为 Eventloop。
最简单的例子:
int main()
{
    int choice;

    do {
        // 等待用户输入
     choice = getch();

        switch (choice) {
        case 'q':
            break;

        case 'a':
            add_record();
            break;
        [...]
        }
    } while (choice != 'q');

    exit(EXIT_SUCCESS);

}
上面这个程序,在一个 while 循环里,根据不同的键盘输入事件,而执行不同的操作。
这就是一个简单的 Eventloop,只是这个 Eventloop 只处理一种事件:键盘输入,且是阻塞等待,虽然很简陋,但是对于上面这个场景而言,已经够用了。
Eventloop 随处可见:
许多开源软件,只要它们有持续运行 + 事件处理的需求,就会有自己的 Eventloop 实现,例如:
图形库 Qt 里的 QGuiApplication::run();
多媒体库 SDL2 里的 SDL_PollEvent();
网络库 Mongoose 里的 mg_mgr_poll();
本质上都是一种 Eventloop,只是由于需求和应用场景的不一样,各自的实现方法有所差异。
合格的 Eventloop:
一个合格的 Eventloop,需要有哪些特性?
我个人认为:
1、不要阻塞,即不要调用可能会阻塞的系统调用,或总是以 nonblocking 的方式调用系统调用。
2、能异步处理事件。
3、性能尽量高,以满足业务需求为下限。
满足上述 3 点,这个 Eventloop 在功能上就是够用的。


2、Eventloop 怎么用?

基于 select 的 Eventloop:
这是来自 UNIX 网络编程 16.2 章节的一个例子,其大致代码如下:
str_cli(FILE *fp, int sockfd)
{
    ...

    // set nonblock
    val = Fcntl(sockfd, F_GETFL, 0);
Fcntl(sockfd, F_SETFL, val | O_NONBLOCK);

    // eventloop
    for ( ; ; ) {
        ...
        Select(maxfdp1, &rset, &wset, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) {
            // do something
        }

        ... // other event
    }
}
这个程序会从标准输入中读取一行数据,然后通过 socket 发送给服务端,然后接收服务端的响应,最后将响应也写到标准输出。
最关键的点是先调用 fcntl 将所有的输入输出都设置为 nonblock,然后用 select 监测所有的文件描述符。
基本上所有的开源事件库,本质上和这个程序没差别。
各种开源的 Eventloop 库:
由于 Eventloop 是一个比较通用的需求,在开源软件里,有许多优秀的异步事件库都实现了这个功能。
比较适合嵌入式领域异步事件库有 3 个:
libevent:an event notification library.
名气最大,应用最广泛,历史最悠久的跨平台事件库。
libev:a high performance full-featured event loop written in C.
较 libevent 而言,设计更简练,但对 Windows 支持不够好,且和开源社区不怎么接轨。
libuv:a multi-platform support library with a focus on asynchronous I/O



点击查看大图
开发 node.js 的过程中需要一个跨平台的事件库,目前非常活跃,推荐大家重点学习。
libev 最精简,入门最容易,这里用它来介绍一下事件库的用法:
#include <ev.h>
#include <stdio.h>

// 定义两个事件 watcher
ev_io stdin_watcher;
ev_timer timeout_watcher;

// io watcher 的回调函数
static void
stdin_cb (EV_P_ ev_io *w, int revents)
{
  puts ("stdin ready");
  ev_io_stop (EV_A_ w);
  ev_break (EV_A_ EVBREAK_ALL);
}

// timer watcher 的回调函数
static void
timeout_cb (EV_P_ ev_timer *w, int revents)
{
  puts ("timeout");
  ev_break (EV_A_ EVBREAK_ONE);
}

int main (void)
{
  // 定义 eventloop
  struct ev_loop *loop = EV_DEFAULT;

  // 初始化 io watcher
  ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ);
  ev_io_start (loop, &stdin_watcher);

  // 初始化 timer watcher
  ev_timer_init (&timeout_watcher, timeout_cb, 2, 0.);
  ev_timer_start (loop, &timeout_watcher);

  // 启动 eventloop
  ev_run (loop, 0);
  return 0;
}
libev 用 watcher 来检测各种事件,当事件发生时,会调用 watcher 的回调函数。
具体支持哪些事件类型呢?



如果你对 libev 感兴趣,可以根据下面这张图阅读 其源码:




3、进一步学习?


本文并没有深入分析各种开源事件库里 Eventloop 的实现,感兴趣的小伙伴,可以看看结合下面这些教程,阅读它们的代码。
Unix 网络编程卷1 / 第16章 非阻塞式IO
https://linuxjedi.co.uk/2020/04/28/event-loop-programming-a-different-way-of-thinking/
http://www.kegel.com/c10k.html
http://www.wangafu.net/~nickm/libevent-book/
https://nikhilm.github.io/uvbook/An%20Introduction%20to%20libuv.pdf
https://gist.github.com/kassane/f2330ef44b070f4a5fa9d59c770f68e9
https://gohalo.me/post/linux-libev.html
https://metacpan.org/dist/EV/view/libev/ev.pod
https://www.youtube.com/watch?v=8aGhZQkoFbQ
https://taoste.github.io/dirtysalt/html/libev.html
https://leezhenghui.github.io/io-strategy/2018/12/01/io-strategy-dive-into-libev-libeio.html
https://www.zhihu.com/question/21796134
原文链接:不懂就问:什么是 Eventloop?
转载自:老吴嵌入式
文章来源于单片机
版权说明:本文来源网络,免费传达知识,版权归作者所有,如涉及版权问题,请联系我进行删除。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 08:45 , Processed in 0.089959 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表