我之前有编写基于事件和轮询的嵌入式系统的经验(适用于没有抢先操作系统的微型MCU)。
在基于事件的系统中,任务通常接收队列中的事件(消息)并依次处理它们。
在基于轮询的系统中,任务以一定的间隔轮询状态并对更改做出响应。
你更喜欢哪种架构?两者能共存吗?
更新:得分
基于投票
-与定时相关的紧密耦合(@Lundin)
*可以使用队列(@embedded.kyle)与事件系统共存
*适用于小型程序(@Lundin)
基于事件
+从长远来看更灵活的系统(@embedded.kyle)
-RTOS版本增加了复杂性(@Lundin)
*小程序=状态机控制(@Lundin)
*可以使用队列和"超级循环"(在控制器/main内部)(@embedded.kyle)来实现
*只有真正的"事件"是硬件中断事件(@Lundin)
相关问题
*寻找有限状态机(@embedded.kyle)的不同调度算法的比较
相关信息
*"更喜欢使用活动对象而不是裸线程"(@Miro)
http://www.drdobbs.com/parallel/prefer-using-active-objects-instead-of-n/225700095
*"正确使用线程=隔离+异步消息"(@Miro)http://www.drdobbs.com/parallel/use-threads-correctly-isolation-asynch/215900465
尽管流行语spitters试图告诉你什么,但在裸机MCU平台上真的没有"事件驱动"这回事。唯一可以接收到的真正事件是硬件中断。
根据应用程序的性质及其实时要求,中断可能合适,也可能不合适。一般来说,使用轮询系统实现确定性实时要容易得多。然而,仅仅依靠轮询的系统很难维护,因为所有定时方面之间都存在紧密耦合。
假设你试图启动一个液晶显示器,它很慢。在空循环中消耗CPU周期时,您可能会决定在此期间通过总线接收一些数据,而不是重复轮询某些计时器。然后你想把收到的数据打印在液晶显示器上。这样的设计在LCD启动时间和串行总线之间产生了紧密耦合,并且在串行总线和数据打印之间产生了另一个紧密耦合。从面向对象的角度来看,这些东西根本不相关。如果你想在未来的某个时候加快串行总线的速度,那么突然之间你可能会遇到LCD打印错误,因为当你试图在它上打印时,它还没有启动完。
在一个小程序中,可以像上面的例子一样使用轮询。但是,如果程序有增长的潜力,轮询将使其变得非常复杂,紧密耦合最终会导致许多奇怪和致命的错误。
另一方面,多线程和RTOS增加了相当多的额外复杂性,这反过来也会导致错误。在哪里划线并不容易确定。
根据个人经验,我想说,除了简单的状态机之外,任何小于20-30k LOC的程序都不会从调度和多任务处理中受益。如果程序变得更大,我会考虑多任务RTOS。
此外,低端MCU(8位和16位苦味)远不适合运行操作系统。如果你发现你需要一个操作系统来处理8位或16位平台上的复杂性,你可能一开始就选错了MCU。我对任何在小于32苦味的产品上引入操作系统的尝试都持怀疑态度。
实际上,事件驱动的编程和线程可以结合在一起,产生的模式被广泛称为"活动对象"或"参与者"。
活动对象(参与者)是封装的、事件驱动的状态机,它们通过相互发布事件来异步通信。活动对象在自己的执行线程中处理所有事件(至少在概念上,如果使用协作调度程序),因此它们在设计上避免了大多数并发危害。
行动者和活动对象(再次)在通用计算中风靡一时(您可以搜索Erlang、Scala、Akka)。Herb Sutter写了几篇很好的文章来解释"活动对象"模式:"更喜欢使用活动对象而不是裸线程"(http://www.drdobbs.com/parallel/prefer-using-active-objects-instead-of-n/225700095)和"正确使用线程=隔离+异步消息"(http://www.drdobbs.com/parallel/use-threads-correctly-isolation-asynch/215900465)
以下是赫伯在第一篇文章中所说的:
"直接使用原始线程很麻烦,原因有很多。。。活动对象通过为我们提供更高级别的抽象和习语,极大地提高了我们推理线程代码和操作的能力,这些抽象和习语提高了程序的语义级别,让我们更直接地表达我们的意图。和所有好的模式一样,我们也有更好的词汇来谈论我们的设计。请注意,活动对象并不是什么新鲜事:UML和各种库都为活动类""提供了支持
所以,这一切真的不是什么新鲜事。但可能鲜为人知的是,尤其是在嵌入式系统社区中,活动对象不仅完全适用于嵌入式系统,而且它们实际上与嵌入式系统完美匹配,而且比传统RTOS轻。
十多年来,我一直在使用事件驱动的活动对象,并为嵌入式系统创建了QP系列的活动对象框架(请参阅http://www.state-machine.com/)。我再也不会回到轮询"超级循环"或原始RTOS了。
我更喜欢最适合当前应用程序的体系结构。
两者可以在多级队列体系结构中共存。一个队列在主循环中以轮询为基础运行。而另一个,最有可能被赋予更高优先级事件的任务,则通过使用基于中断的抢占来工作。
有关不同调度算法的更详细解释和比较,请参阅我对这个SO问题的回答。