算法部署---整体架构


该模块是算法的部署部分,对软件模块只提供调用的头文件、动态库文件及参数配置文件。

模式设计

在头文件中声明交互接口的结构及涉及到的联合数据类型,动态库文件实现了头文件定义的接口及流程上涉及的功能,参数配置文件描述了算法运行过程中所需的外部参数。该软件架构用到的如下设计模式:

单例模式

算法接口设计采用单例模式,在同一线程中只能实例化一个控制对象,直到对象的生命周期结束。

  1. 原理 该类的构造函数定义为私有方法,同时通过该类提供的静态方法来得到该类的唯一实例。
  2. 实现 调用该方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
  3. 优缺点比较
    3.1 饿汉模式,在类加载的时创建一个静态对象,线程安全但是占用系统的资源。
    3.2 懒汉模式,在使用时进行创建,多线程是安全的。
    3.3 双重校验锁,避免多线程得到不安全,但是多线程同步开销大。

观察者模式

与软件层实现数据的交互,采用异步非阻塞的通知方式。当算法处理完成后,推送结果给用户层。

  1. 原理 该模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主体对象发生变化时,观察者都会得到通知,并且自动更新。
  2. 实现 该模式如下四个角色
    2.1 抽象主题(Subject),维护一个观察者对象集合的操作方法,对集合的增加、删除
    2.2 具体主题(ConcreteSubject),在具体主题的内部状态改变时,给所有登记过的观察者发通知。
    2.3 抽象观察者(Observer),为具体观察者提供一个更新接口。
    2.4 具体观察者(ConcreteObserver),实现抽象观察者提供的更新接口。

工厂模式

此部分参考caffe的工厂模式设计,属于创建型模式。对象时不会对客户端暴露创建逻辑,通过使用一个共同的接口来指向新创建的对象。

  1. 原理 ,定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
  2. 实现,将类的创建和类的使用分离出来,当 Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
  3. 不同工厂模式的同异
    3.1 简单工厂,工厂类中有一个方法,通过switch中不同的值来创建不同的对象并返回,通常这个方法是一个静态方法。
    3.2 工厂方法,不只是为了封装对象的创建,而是要把对象的创建放到子类中实现:Factory中只是提供了对象创建的接口,其实现将放在Factory的子类ConcreteFactory中进行。
    3.3 抽象工厂,存在多个抽象产品类,每个抽象产品类可以派生出多个具体产品。

多线程

每个相机的算法类与单个线程相绑定,有数据时,算法的处理线程被唤醒。处理完成后,该线程进入休眠状态,降低系统的负担。

线程属性

线程作为调度和分配的基本单位,进程作为独立分配资源的单位。线程参与操作系统的调度,参与CPU的竞争,得到分配的时间片。而进程负责获取操作系统分配的资源,如内存。
线程进程之间的区别,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,线程间通信机制更方便。

  1. 多线程 ,Linux系统下的多线程遵循POSIX线程接口,称为pthread。
    1.1 函数pthread_create用来创建一个线程
    1.2 函数pthread_join用来等待一个线程的结束
    1.3 函数pthread_attr_t用来修改线程的属性
    1.4 函数 pthread_exit() 终止线程
  2. 线程状态 ,线程的生命周期大体可分为5种状态
    2.1 新建(NEW):新创建了一个线程对象。
    2.2 可运行(RUNNABLE):该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
    2.3 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
    2.4 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行.
    2.5 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

线程同步

Linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。

  1. 互斥锁 ,锁机制是同一时刻只允许一个线程执行一个关键部分的代码。
    1.1 互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会锁上变成lock状态,此后只有该线程由权力打开该锁,其他线程想要获得互斥锁,必须得到互斥锁再次被打开之后
  2. 条件变量 ,当线程在等待满足某些条件时使线程进入睡眠状态,一旦条件满足,就换线因等待满足特定条件而睡眠的线程。
    2.1 互斥量不是万能的,比如某个线程正在等待共享数据内某个条件出现,可能需要重复对数据对象加锁和解锁(轮询),但是这样轮询非常耗费时间和资源,而且效率非常低,所以互斥锁不太适合这种情况。
    2.2 无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求(用 pthread_cond_wait() 或 pthread_cond_timedwait() 请求)竞争条件(Race Condition)。
  3. 信号量 ,互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区。

编译部分

Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。

编译与链接

程序要运行起来,必须要经过四个步骤:预处理、编译、汇编和链接。

  1. 预处理 ,展开头文件,宏替换,去掉注释,条件编译
  2. 编译 ,通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。
  3. 汇编 ,把作为中间结果的汇编代码翻译成了机器代码,即目标代码。通常一个目标文件中至少有两个段,代码段和数据段。
  4. 链接 ,将源文件中用到的库函数与汇编生成的目标文件.o合并生成可执行文件。静态库和应用程序编译在一起,在任何情况下都能运行,而动态库是动态链接,文件生效时才会调用

makefile

make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

  1. 规则 ,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
    target … : prerequisites …
    command
  2. 如何工作 ,在默认的方式下,也就是我们只输入make命令。
    2.1 make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
    2.2 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
    2.3 如果target文件不存在,或所依赖的后面的 .o 文件的文件修改时间要比这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
    2.4 如果所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。
    2.5 C文件和H文件是存在的,于是make会生成 .o 文件,然后再用 .o 文件生成make的终极任务。
  3. 符号规则
    3.1 $@ 扩展成当前规则的目的文件名
    3.2 $^ 扩展成整个依靠的列表(除掉了里面所有重 复的文件名)
    3.3 $< 扩展成依靠列表中的第 一个依靠文件
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值