flint设计---多任务调度

Flint的设计目的是制作一个微型应用框架,可以用来随时无侵入性的嵌入到项目中,快速优雅而且小而美的解决一些不值得大动干戈的问题,或者让因为各种限制而不能使用高级特性和三方库的开发(如低性能无法完成线程管理的CPU)变得更加快速而容易。目前已经在我个人开发的大部分嵌入式项目中得到应用,并且极大的提高了流程可控性、性能,极大的降低了调试难度。其中,多任务调度处理就是核心部分之一。

如何处理多任务同时执行的问题,看似通过线程创建,实则在特定与通用场景下,一直面临几个问题:

  • 如果存在多线程共享变量,引发的并发冲突只能通过加锁之类低效方式来处理
  • 多线程涉及到堆栈切换等本身是存在调度开销的,在性能敏感的主机上可能会相对较重
  • 实时性可能会难以保证,同时调度节奏并不不可控,在低于ms响应时间要求的部分可能难以达标
  • 会产生设计惰性,在部分执行代码中,可能会引入一些会导致长时间阻塞的操作从而引发难以排查的性能问题。
  • 线程管理与监控调试的难度将随数量线性增加。
  • 最后,如果CPU不支持多线程OS的话,如何实现?

为了解决这些问题Flint采用了以状态机为基础的多任务轮转调度模型。其中,每个任务均通过返回值自行控制下次调用延时(us级)为单位,各任务采用单线程轮转调度独占运行。这样有几个明显目的与好处:

  • 所有任务调用每次都是独立的,不会出现同时多个任务执行的情况。
  • 从设计上就要求每个任务的每次调用均需要快速完成,不能出现任何可能的阻塞,从而根本上避免多线程同时读写冲突问题,而且任何一个函数体的任何一次执行都是无中断从头至尾。
  • 基于状态机的控制,可以很容易获取当前所有任务的执行状况,并且单个任务的失败并不会导致其他任务的失败。
  • 无需多线程维护机制,执行效率高。
  • 由于单任务单次执行耗时极短,一般在低性能MCU上也可以达到50us-1ms左右的调度控制粒度,针对这类实时需求不再需要定时器辅助。
  • 理论上可以大量扩充任务数,多任务情况(长周期任务影响更小)基本不会引起对性能和内存的影响。一般的线程模型往往有线程限制,尤其是开销随数量增长很快。
  • 由于任务调度机制的特点,多任务共享变量并不需要担心并发访问问题,所有的访问操作均为互斥执行,极大的降低调试难度。
  • 非常易于扩展,可以极为简单的动态注册与删除或者失活线程。
  • 对存储要求很低,纯C实现,极易轻量级整合,也可以简单生成多个实例。
  • 所有相关内存(任务内除外)均为栈上预分配,无动态内存分配需求。

具体实现可参考代码(部分)flint_thrd