在RTOS中替换队列


12

为了进行任务间通信或在RTOS的两个任务之间共享数据,我们使用队列。但是队列的问题是它们的速度很慢。...他们先将数据复制到Buffer中,然后再进行Mutex处理,然后再进行数据传输。如果必须传输大数据,这会很烦人。另一个问题是,如果多个任务访问同一队列。然后图片变成这样:-首先等待访问Queue,然后对内部Mutex处理队列,然后进行数据传输。

这增加了系统的开销。什么是队列的有效替代品

(我想这个问题是我们使用的RTOS的独立性。大多数RTOS仅以这种方式处理队列)


由多个任务访问的队列是什么意思?您是要发布到队列中还是从队列中读取?多个任务应该能够以最小的开销发布到队列中。RTOS应该处理静音,以便发布是原子操作。对于99%的任务,您应该有一个循环插入队列并处理消息。队列(通常)只能由一个任务读取。您可能需要查看设计以及如何使用队列而不是替换队列。
Erik

@Erik:对不起!我正在使用您提到的机制...。我想说些其他的话,然后写了不同的内容...。我将对其进行编辑!感谢您指出错误!我正在等待代码中的队列访问!
Swanand 2011年

Answers:


7

队列以这种方式运行,因为这是用于任务间通信的线程安全事务模型。在任何不那么严格的方案中,您都有数据损坏和/或所有权问题的风险。

您是将数据复制到内存中的缓冲区中,然后将指针与队列元素一起传递,还是试图将队列元素本身中的所有数据传递?如果您不传递指针,那么这样做将提高性能,而不是一次通过队列元素传递一个字节。


2
我打算说同样的话。如果仅将指针传递到队列中的数据,则可以提高速度,但请确保不要以尝试使用和更改数据的两个线程结束。
2011年

正如@Kortuk所说,我需要“确保不要以试图使用和更改数据的两个线程结束”……这意味着开销增加了……我不需要太多处理!:(
Swanand 2011年

因此,没有这样的队列替代方法...我需要使用指针队列来代替数据队列!
斯旺德2011年

1
@Swanand如果计划您的应用程序,以使队列仅是单向的(即,您从未在两个任务中读取相同的队列),并且您立即处理了指针处存储的数据,然后将其释放,则共享数据应该没有问题。由于您可能必须创建多个队列才能可靠地来回传递数据,因此开销将会增加,但这是在多任务环境中开展业务的成本。
AngryEE 2011年

7

一种简单的方法是将一个指向数据的指针放在队列上,并使用该指针使用数据。

请注意,您要以这种方式为性能而进行安全性交易,因为您必须确保:

  1. 缓冲区保持有效,直到使用者使用了数据
  2. 有人取消分配缓冲区

如果您不使用动态分配的内存,则不必取消分配内存,但仍必须确保在消耗数据之前不要重用内存区域。


6

可以针对单个生产者/单个消费者的情况实现无锁队列,并且通常您可以设计软件以最小化多个生产者或多个消费者队列的数量。

可以这样构造无锁队列:分配要通信的元素的数组,还有两个整数,分别称为Head和Tail。Head是数组的索引,将在其中添加下一项。尾巴是数组的索引,可以删除下一项。生产者任务读取H和T来确定是否有添加项目的空间。将项目写入H索引,然后更新H。使用者任务读取H和T,以确定是否有可用数据,从索引T读取数据,然后更新T。基本上,这是两个任务访问的环形缓冲区,并且操作顺序(插入,然后更新H;删除,然后更新T)可确保不会发生数据损坏。

如果您有多个生产者和一个消费者,或者一个生产者和多个消费者的情况,那么您实际上会有某种资源限制,除了使用同步之外,别无其他,因为性能限制器更有可能比起带有锁定机制的OS开销,它是唯一的生产者/消费者。

但是,如果您有多个生产者和消费者,则值得花时间(在设计空间中)看看您是否无法获得更协调的沟通机制。在这种情况下,通过单个队列序列化所有内容肯定会使队列的效率成为性能的中心决定因素。


1
我本来打算为此+1,但您是不对的:无锁队列可以为多个读取器和写入器实现,它们只是更复杂。(看看迈克尔·斯科特+对无锁队列纸google.com/search?q=michael%20scott%20queue
贾森小号

1
@Jason S-Scott纸是否专门要求重新进入无锁插入和移除操作?如果可以,请提取并发布,对于许多人来说,这将是一笔宝贵的资产。读者应注意,所引用的论文使用了特殊的机器说明,而我在以上职位中的职位并未假定此类说明。
JustJeff 2011年

1
是的,无锁算法的成本通常依赖于CAS或等效指令。但是,重新进入在这里如何发挥作用?它对于互斥体+锁定结构有意义,但对于数据结构操作却没有意义。
詹森·S

2

如果队列本身保存的项目足够小,可以与不包含负载存储,比较交换或类似原语的项目一起使用,则可以在无锁的多生产者单消费者队列中获得有效的操作,并且可以使用一个空队列插槽的保留值或保留值。当写入队列时,写入器进行一个比较交换,以尝试将其数据存储到下一个空插槽中。如果失败,则编写器尝试以下插槽。尽管队列维护着指向下一个空插槽的指针,但指针值为“ advisory”。请注意,如果系统使用比较交换而不是专用于负载存储,则可能需要具有不同“空插槽”值的“系列”。否则,如果在编写者发现空队列插槽并尝试对其进行写入的时间之间,另一位作者写入该插槽,而读者读取该插槽,则第一位作者在不知不觉中将其数据放置在读者看不到的位置。在使用独占负载存储的系统中不会发生此问题,因为独占存储将检测到即使已将数据写回到旧值也已写入的数据。


1

您可以通过在队列顶部进行写入来更有效地访问队列。通常,大多数RTOS确实都支持将其添加到队列的前端,而无需获取互斥体。但是请确保在只想更快地执行数据的地方使用添加到队列前面的内容尽可能少。通常,队列结构具有最大大小限制,因此您可能不会将所有数据放入队列中,因此传递指针总是很容易的。

干杯!!


1

队列并不是天生就慢。它们的实现可能是。

如果您盲目地复制数据并使用同步队列,则会发现性能下降。

正如其他海报所指出的,有无锁替代品。单生产者/单消费者的情况很简单。对于多个生产者和消费者,Michael和Scott的无锁队列算法(它们是他们的姓氏)是标准,并且用作Java的ConcurrentLinkedQueue的基础。

在某些情况下可以优化对队列的需求,但是它们提供了并发保证,通过允许您解耦任务,通常可以为系统带来巨大的简化收益。


在Michael&Scott的论文中:“对于提供通用原子基元(例如比较和交换或加载链接/存储条件的机器)的机器,这是显而易见的选择算法”。尽管这实际上可能无法完全锁定线程,但是这里有一种同步形式。
JustJeff 2011年

你有一定道理; 它可能会减少从独占访问内存屏障的并发要求。
詹森·S
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.