• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2023-09-18 00:07 Aet 隐藏边栏 |   抢沙发  10 
文章评分 1 次,平均分 5.0

背景

  1. 管理器用于管理多个任务
    1. 接入了管理器的模块,会根据自己要做的事情,来生成不同的任务
    2. 而这些任务的生成,是由接入了管理器的模块,通过一些重要的数据(比如目标数据包的编号,以及目标所在的位置,要求管理器执行的任务类型),来让管理器生成的不同的任务,并添加到任务队列中
    3. 目前的设计的框架是支持多种任务的,但目前仅实现了安装、卸载、解压三种任务
  2. 通俗来讲,接入了管理器的模块,告诉管理器要处理的对象的编号,要处理的对象的所在位置,以及这个任务的任务类型,由管理器具体去执行其他操作比如生成一个任务、执行任务、管理任务等等,不管执行成功失败,再由管理器把结果通知到接入了管理器的模块

设计与实现

状态通知

  1. 为了让管理器能做到将任务状态通知给接入模块,设计如下:
    1. 让接入模块继承了管理器的委托模块,这样从继承的角度来看,接入了管理器的模块自己就是一个委托模块,于是管理器可以在适当的时机委托接入模块去做一些事情
    2. 比如,当安装功能完成时,管理器自己是知道的,但是接入了管理器的模块是不知道已经完成了,这时管理器可以委托接入模块去做一些安装成功后该做的事情,也就是相当于通知接入模块某件事达成了,去接管后续

单例管理器

  1. 由于接入模块可能在不同的线程中对管理器添加任务,而我们希望由管理器接管处理所有的任务,这种情况下,就需要接入模块的不同线程操作的都是同一个管理器对象
  2. 于是,我把管理器设计成了单例的,只在第一次进行初始化,后面通过接口去获取到的管理器,都会是同一个
    1. 可以看到这个类的拷贝构造和拷贝赋值操作符都是delete了,并且构造函数是private
    2. 这样就只能通过给出的public接口get_helper来获取这个管理器的实例了

  1. 可以看到get_helper是一个static的函数
    1. 并且通过指定了一些内存序来获取或创建这个管理器
    2. 第一次指定了memory_order_acquire的意思,就是确保如果在另一个线程里面已经完成了对这个管理器的初始化(new appcenter_helper_file(app))之后的所有写入操作对当前线程是可见的
    3. 第二次用memory_order_relaxed是因为我们已经在一个锁的保护下,所以不需要额外的内存顺序保证。换句话说,当已经获取到了锁的保护了,这样读的时候,是不会有其他线程在写的,所以用最宽松的内存序也是可以了
    4. 第三次用memory_order_release是因为,我们正在写,我们需要确保所有的写操作在存储到m_helper之前都完成,让任何看到m_helper不为空的线程看到的m_helper是一个完成初始化的对象

添加任务

  1. 从上面可以看到,管理器提供了一个add_task接口,这个接口需要3个参数,然后,根据任务类型,任务将被添加到不同的任务列表里
    1. 当然,在添加的时候,使用了互斥量进行了同步
    2. 可以在下面代码中看到,安装和卸载被添加到了同一个任务列表中了,而解压被添加到了另外的任务列表中了,这是根据这些活动占CPU的不同,在设计上对它们进行了分类
    3. 设计为:低CPU任务由单独的线程处理,高CPU任务也由单独的线程处理,这两个线程并发运行。但是,这两个线程同时只能运行一个任务
    4. 当然,当前框架稍作修改,就可以改为每类任务能同时运行多个,之所以按上述设计,是业务需求
  2. 每当一个任务被添加到对应的任务队列,就使用条件变量去唤醒对应的线程
    1. 并且,管理器记录当当前正在处理的低类和高类任务,所以在添加任务时,对相同的任务进行了过滤

低CPU线程

  1. 可以看到,这个线程处理低CPU任务,由于每个任务标记了任务类型,所以在这里,可以根据任务的类型对不同的任务进行了处理
    1. 目前的实现可以看到仅仅把安装卸载归为低CPU任务,并在这里进行了处理
  2. 至于安装卸载的具体事项,见其他文章

高CPU线程

  1. 可以看到,这个线程处理高CPU任务
    1. 目前仅把解压归为高CPU任务
  2. 同样,解压具体实现,见其他文章

线程初始化

  1. 由于需求相关,所以在设计上并没有采用线程池的做法,而是直接创建了两条线程用来处理不同的任务

管理器的清理

  1. 由于管理器是被其他模块接入的,所以管理器的清理工作,肯定是在其他模块的清理时机去做才比较合理,如下:
    1. 管理器提供了static方法th_cls,给外部接口去调用,这个函数的功能是获取管理器类的指针后,用delete去释放
    2. 从而,会调到管理器的析构函数
    3. 而真正的资源清理,是在管理器的析构里面去做的

管理器析构

  1. 可以看到,先修改了一个原子类型的变量,置为true
  2. 然后调用了notify_one来对应的线程去检测条件
    1. 如果线程此时在休眠,那它会被唤醒,检查谓词后退出
    2. 如果现场正常运行,已经是被唤醒了,那么notify_one不会产生影响,这时候,就需要正常执行任务的线程在某个状态时做某些处理,从而及时结束任务

  1. 然后,判刑线程是否可连接,如果可连接,调用join等待线程退出,这将导致当前线程被阻塞,知道这个线程完成了任务
  2. 在两个线程都退出后,检查任务队列里面是否还存在任务,如果存在,要用delete删掉任务,因为这些任务在添加的时候是被new出来的

线程的状态恢复

  1. 当一个任务完成后,调用之前介绍的委托接口,来通知任务的完成状态
  2. 通知完后,上锁,将当前保存的任务设置为空,将线程运行标志清空(这个点是否可以优化),将当前线程对应的任务归类标志设置默认
  3. 调用notify_one唤醒线程,让他接着处理其他任务

相关优化点

清理线程时,任务正常进行

  1. 清理线程时,任务正在进行,那可以让线程在执行任务期间,检查某个状态,如果发现状态被设置了,那么就取消任务的后续执行

线程运行标志

  1. 只有一个线程的情况下,只要这个线程被唤醒了,那么再调用notify_one通知这个线程处理任务,是不会产生影响的,或者是说没有任何效果
  2. 所以上述设计中,线程运行标志似乎有点多余

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享