EmxUV

Euv部分是针对Emx开发人员需要使用的部分,SDK的使用者需要了解本小结中关于EuvLoop的使用

Emx的日常编程中会经常使用到Euv提供的各种组件,需要开发人员熟练掌握Euv的使用方法和内部原理

  Euv模块是对开源libuv库的C++封装,其拥有跨平台的特性,内部使用select/epoll/kqueue/IOCP等技术来完成IO的多路复用,Emx的整体架构都是基于此特性构建出来,关于libuv的详细介绍可参考libuv

  Euv的核心是EuvLoop,一个EuvLoop就是一个线程,是各路IO复用的载体,负责如EuvAsync/EuvDns/EuvTimer/EuvTcp等句柄(事件)的监听和运行,EuvLoop启动运行后,对参与EuvLoop循环的所有句柄的操作都必须在EuvLoop内进行(EuvAsync除外),否则将面临线程安全的问题。在EuvLoop启动之前可以注册若干个事件,启动后也可在loop内执行注册事件操作(不可在loop外执行),EuvLoop内部使用select/epoll等去监听这些注册的事件,举个EuvTimer定时器的例子

#include "EmxCore.hpp"
// Emx2.0统一使用Emx作为顶级命名空间
using namespace Emx;
//一个定时器的例子
class Timer {
public:
//启动例子
void Start() {
//初始化一个EuvLoop,并将EuvLoop命名为"TimerExample",
//这个名字可以通过pstree命令看到
//使用lambda表达式来定义当此EuvLoop结束时需要做的操作
//注意:想要EuvLoop顺利退出,必须在这里关闭此EuvLoop下监听的所有句柄才行
m_loop.Init("TimerExample", [this]() {
//StopAndDeInit执行后会在某次循环中运行这里
//销毁timer
m_timer.Destroy();
});
//创建并初始化一个timer,绑定到m_loop上运行
m_timer.Create(m_loop);
//启动(注册)timer,将启动时间定为1000ms,表示循环启动后的1s会触发超时回调
//将重复间隔定位2000ms,表示第一次超时后每隔2s将再次超时
//使用lambda表达式定义一个timer定时器超时触发的回调,也可使用下面的bind方式
//m_timer.Start(1000, 2000, std::bind(&Timer::OnTimer,this));
m_timer.Start(1000, 2000, [this] { OnTimer(); });
//启动EuvLoop线程,循环开始
m_loop.Start();
}
//结束例子
void Stop() {
//结束并关闭一个EuvLoop,此时会导致EuvLoop Init时注册的OnQuit回调被调用
//然后等待EuvLoop监听的所有句柄都被关闭了,EuvLoop就顺利退出了,否则会一直阻塞在这里
//此函数可以在EuvLoop之外的线程调用,例如此例子中就是在main线程调用的此函数,而不是EuvLoop线程
m_loop.StopAndDeInit();
}
private:
void OnTimer() {
//此打印将在EuvLoop启动后1s时打印一次,然后每隔2s打印一次
printf("%s:%d reached timeout\n", __FUNCTION__, __LINE__);
//注意类似OnTimer这种注册的回调函数都是运行在EuvLoop线程中的
//在这些回调函数内部可以使用如m_timer.Create/m_timer.Start/m_timer.Stop/pipe.Create等操作句柄的参数。
//当EuvLoop运行起来后,在EuvLoop外,也就是其他线程中是不允许操作这些函数的,唯一的例外是async.Send()函数
}
private:
EuvLoop m_loop;//定义一个EuvLoop循环
EuvTimer m_timer;//定义一个定时器
};
int main(int argc, char *argv[]) {
Timer timer;//定义Timer对象
timer.Start();//启动Timer
sleep(8);//等一等
timer.Stop();//结束Timer
return 0;
}
Definition: EmxGpio.hpp:10

  必须注意的是,因为一个EuvLoop就是一个线程,所以所有注册在这个loop中的事件回调都是按照事件的发生顺序依次执行的,虽然这使得loop内的各个事件之间共享资源变得更加安全,但同时也意味着任意事件回调处理的阻塞都将阻塞后面的所有事件,所以需要留意回调函数内部代码的阻塞问题。Emx中出现的所有需要绑定EuvLoop的回调函数都需要注意这个问题,例如异步实时流获取,SD卡状态回调等等。