当前位置: 首页 > 工具软件 > itachi > 使用案例 >

itachi学习

钱经赋
2023-12-01

itachi是偶然在网上看到的dirtysalt 编写的异步编程框架库

 

https://github.com/dirtysalt/dirtysalt.github.io/blob/master/codes/cc/itachi/itachi.cc

 

异步框架是用来解决什么问题的?

当我们说异步框架时,一般指的是注册回调函数的方式来完成一件事,最简单的例子,比如创建一个线程就是典型的异步思想,我们会先写好入口函数,然后create thread时输入函数指针和参数

异步框架也类似,先定义一系列函数,指定在什么事件发生后,做什么事情,然后把这些函数交给某个异步对象,他会负责监听对应的事件,并在合适的时候调用对应的函数

异步框架有什么好处?

如果不用异步框架,比如多线程,阻塞io,这样会频繁的线程切换,cpu利用率不高,使用异步框架,可以尽可能的提高并发能力(socket),或者提高吞吐量(读写磁盘)

itachi的主要组成

itachi是基于libev的基础上,封装了一个itachi类,itachi包含两个线程池,一个处理io,一个处理计算类功能。

 

主要包括几个类

AsynchClient

此类主要包括一个回调函数,使用者继承此类并定义自己的回调函数, 实现了job的逻辑

AsyncContext

context代表了具体的任务,所谓任务,其实执行的都是任务对应的client中定义的cb,只是参数不同,这些参数,就是context对象记录的内容,比如这是一个什么类型的任务,io的还是cpu的,如果是io的,本任务关注哪些事件等等。这些context会放在线程池中每个线程的queue中,线程依次取出context,然后执行context对应的client的cb,并把context作为参数。

context记录了job的参数

handler

线程池的基类,纯虚类,将线程入口函数定义为虚函数,子类可以自己定义入口函数。作为一个线程池的基类,实现的关键是,在启动多个线程时,线程入口函数如何执行子类定义的函数,很明显应该使用this指针,把this指针当作线程入口函数的参数(一部分)传入,这样就可以在入口函数中执行this->thread_function, 从而调用到子类的入口函数。

cpuhandler

继承handler,每个线程独享一个任务队列,此类重写线程入口函数,内容就是死循环,从自己的队列中取任务,执行,需要注意的是, 每次取任务,都会把队列中当前的任务全取出来, 这样做的目的不懂,如果任务比较多, 任务会倾斜,而不是均分。

pollhandler

继承自handler, 也就是说本质上也是一个线程池, 在线程入口函数中, 一个死循环, 从此线程对应的任务queue中取出asyncContext,执行对应的逻辑,但是与cpuhandler不同的是, 对于取出的asyncContext,并不是执行其对应的client中的onComplete,而是将此context对应的fd和event, 注册到ev loop中,ev_loop会在有对应的事件发生的时候,执行对应的回调函数, 注意,ev_loop中注册的回调函数,依然不是context的回调函数,而是pollhandler自己定义的net_callback, 这个net call back, 会把此context 转交注册到cpuhandler中, 也就是说, 将一个io任务lanch到itache之后, 先是根据其type(IO),交给poolhandler的某一个线程池的队列中,pollhandler对此任务的处理,就是将其注册到ev-loop中,并且在回调函数中,将此context转交给cpuhandler, pollhandler只会做poll, 而不会有读写逻辑,cpuhandler负责读写。

 

对于此库的使用者, 一般是继承AsyncContext 和AsyncClient , 在context中汇总上下文, 比如对于socket来说,就是fd, 缓存区等, 在client中实现回调onComplete, 回调函数被cpu handler中的线程执行, 对于socket来说,这个执行一般就是根据具体的action type , 要么执行:: write , 要么执行::read 等系统调用, 读, 是读到缓存中, 写也是从缓存写到socket

 

此处使用了libev, 主要类型和函数包括

struct ev_loop

struct ev_async

一个 ev_loop对应着一个ev_async

每次往线程池加一个任务,就会调用一次 ev_async_send(loop_[id], async_ + id);  析构时也会对每个looper执行一次,

调用此函数, 相当于noticy一次, 将ev_async注册到loop上之后,阻塞对应loop的ev_run上, ev_async_send可以触发ev_run解除阻塞(ev_run的第二个参数是EV_ONCE, 即有一个事件发生,就会解除阻塞,并继续往下执行) 

ev_run

ev_once() 这个是关键的一个函数, 将fd 和事件注册到 loop中,并提供一个回调函数

 

 

 类似资料: