IApplication 用户文档¶
快速入门¶
创建应用实例¶
为了快速了解IApplication,我们以实例入手,写一个最简单的例子:
说明:
- 构造函数自动解析命令行参数,初始化 ASIO 上下文。
run()启动事件循环,阻塞直到应用退出(如收到Ctrl+C)。
在上述代码运行的时候,console 输出如下:
在 console 中输出了 IWebCore 的变体内容。
添加详细的任务输出¶
我们将代码添加一句,如下:
$EnableTaskOutput(true) 这是一句宏注解,关于宏注解的内容,可以参考具体的文档。当添加这一句话之后,在console中输出如下:
上述内容多出来的是任务信息,这个都是在 ICore 中默认注册的任务。 平时他是不显示的,但如果有$EnableTaskOutput(true)这一句宏注解的话,这些任务信息都被输出出来。
所以用户可以理解,为什么我们没有添加任何的输出语句,而且 IApplication 中也没有输出 IWebCore 的相关代码,但是这个信息却被输出出来。就是因为IWebCore::IBannerTask 被注册到系统当中,在任务执行的时候执行了出来。
关于宏注解和 任务,我我们接下来会深入的讲解.
关于 IApplication¶
为什么要有 Application¶
在很多的程序中,都有 Application 的概念,Application 在程序一开始启动,以 exec() 或者 run() 开启事件循环,防止程序意外退出。在事件循环之前,Application 同样会初始化整个程序必要的运行环境。
ICore 中的程序也是一样, 是应用程序的核心框架,提供以下核心能力:
- 初始化流程规范化:自动解析命令行参数(
argc, argv)、加载基础配置(如线程数、单元测试开关)、创建 Qt 环境兼容层(QCoreApplication)。 - 异步任务调度 基于 ASIO 的事件驱动模型实现高并发处理
- 资源集中管理:通过单例模式(
IAsioContext、ITaskManage)保证 ASIO 上下文、定时器、异步任务等核心资源全局唯一,避免重复创建和资源泄漏。 - 跨平台支持:封装 Windows 的
Ctrl+C信号和 Linux 的SIGINT/SIGTERM处理逻辑,提供统一的优雅退出机制。 - 基础设施集成:提供定时器、配置管理、单元测试等开箱即用的组件
- 提供事件循环: 通过 asio 的事件循环来防止程序退出,并提供一些异步执行功能。
IAsioApplication¶
在 ICore 中,我们将 IAsioApplication 命名为 IApplication。
ASIO 实现的核心应用类,继承自 IApplicationInterface,提供完整的事件循环能力。如下是他的声明
IApplicationInterface¶
应用程序接口模板类,定义基础框架规范,如下是他的声明:
功能¶
运行注册任务(Task)¶
在IApplication 中,最重要的一个目的就是运行已经注册到程序当中的任务,这些任务是程序运行的主体,提供一个个功能点, 如我们上述 IBannerTask, 他的功能就是 输出 IWebCore信息到 console 当中。 具体的内容可以参考 Task 相关的文档. 而运行这个任务的方式也很简单,在 run 函数中调用如下的代码:
通过上述的第五行代码,用户注册的功能,就可以在框架中调用. 比如用户注册了一个数据库,那么这个数据库就会在这里被初始化; 用户注册了一个Controller 对象, Controller对象也会在这里被挂载掉 ControllerMapping 中去,在后续会被 Http 请求查询,调用; 用户注册了一个 Bean, 那么 这个Bean 就可以在之后被用于 json 的自动相互的转换;系统注册了一个 IBannerTask, 在 Console中就会输出 IWebCore 字样的内容。
提供基于 Asio 的事件循环¶
ICore 的 IApplication 在一开始是基于 QCoreApplication, Qt 提供事件循环。在后来的开发过程中,逐渐替换成基于 asio 的事件循环。这样可以提供更为强劲的多核事件循环机制,并且变得更加通用。
如果用户有基于 Qt 事件循环或者其他任意类型的事件循环的需要,可以自行实现自己的 IApplication。
在 ICore中,asio 事件循环被封装在 IAsioContext 中,在 IAsioApplication 中,这样调用 asio 的事件循环:
在第3行,我们添加事件响应机制,可以通过 ctrl c 退出程序。在5-8行,计算出一个合适的 线程数量。在第9行,则开始事件循环。这个时候,就可以像用户提供相应的,注册在 程序中的服务了。
线程数量¶
在程序中,如果用户没有设置线程数量,则默认线程数 = std::thread::hardware_concurrency() * 2 ,硬件支持线程数的2倍。用户设定线程数量可以通过如下的方式:
这里展示两种线程的设置方式。在第三行,我们通过宏注解的方式设置线程数:$SetAppThreadCount(5) 在第6行,通过代码的方式设置线程数 app.setThreadCount(4); 最终,这里代码的优先级更高一些,线程被设置为4个。
此外,用户可以通过配置的方式设置具体的线程数量。关于配置,请参考 Config 模块。
参数和 软件路径¶
ICore 自动解析命令行参数,用户可以通过 arguments 来获取用户输入了什么参数, 如下:
ICore 同样也提供了一个applicationPath 告知用户当前的 exe 路径。
提供非精确时间戳¶
在c++中,对于高性能程序而言,时间的获取是一个昂贵的操作。但是程序往往需要的不是一个精确的,具体的时间,而是一个大致的,相对的时间。这里大致时间的概念是,这个时间精确到一个地步,比如精确到秒,这个单位就行, 相对的时间的概念则是,我每一次获取的时间必须是不同的,后一次获取的时间必须比前一次获取的时间晚一点。
ICore 的application 中提供了这样的时间。它是一个 64位 atomic整型的数据,一个数字表示1纳秒, 时间是相对于 epoch 时间,也就是这个数字是相对于 1970年1月1日0时整 过去了多少纳秒的时间。这个时间数据每秒自动更新一次。当用户每次请求他的时候,他在原本数字的基础上加 1,返回给用户。
所以这里为什么用纳秒,就是因为我判断,没有什么程序能够在1s 中请求1000,000, 000 次 (这个数据时10亿, 10的9次方),如果真的有了,这个需求就不是 ICore所能处理的需求,这个也不在我的考虑范围中了。
这个时间可以通过 IApplication::time() 函数来获取,它适合日志时间戳等非高精度需求。
其他内容¶
IAsioContext¶
IAsioContext 的部分声明如下:
在这个函数中,我们可以看到 第6行,提供一个异步发布任务的功能。
在第7行,可以启动一个 定时器,得到一个句柄。
在第8行可以根据这个句柄来停止这个timer。
为什么是 std::ptrdiff_t 类型呢?这个是因为我们本质上是 new 了一个对象,将对象地址转换为 std::ptrdiff_t 类型返回的。stopTimer 中也是将这个 std::ptrdiff_t 重新转换为指针再进行后续的操作的。
他的示例如下: