在硬件应用程序中使用共享内存(C++)时需要互斥



我有一个传感器,它以n Hz(比如10Hz=每秒10次)的频率将数据写入线程中的共享内存。一个单独的线程正在读取这些数据并使用它来获取一些结果。阅读器线程的频率不同。它可以更慢,例如每秒8次,也可以更快,例如每秒15次,这取决于正在计算的内容。读取器线程只是从共享内存中读取数据。它不修改数据(只处理数据以获得一些结果),也不向共享内存写入任何内容。整个过程非常巧妙。我不在乎同步,因为阅读器只会在需要的时候读取共享内存中的内容(它会轮询数据)。如果在两次读取之间,共享内存的内容发生变化,则读取器将使用新数据。如果在两次读取之间,共享内存的内容没有改变(如果读取器比写入器快得多),那么读取器只使用共享内存中的任何数据。

现在,我的同事告诉我使用互斥锁同步对共享内存的访问,但我不同意。原因是,如果我使用互斥来控制访问,写入程序写入共享内存的频率会有所降低(当读线程锁定了互斥并进行写入时)。在未来,我们将有更多的读取器线程,我担心写入器线程写入共享内存的频率将进一步降低,因为将有另外两个线程竞争互斥。

我知道种族条件等,但我觉得种族条件和SO上给出的许多例子以及其他网站都考虑了与我不同的场景:例如,当两个线程正在读取和处理银行余额,而一个线程读取速度较慢或较快,余额最终出错。。。结果是2000美元而不是1000美元。然而,在我的情况下,"银行余额"——要共享的数据是由传感器生成的。值的任何变化都是由于物理原因造成的,要共享的数据值永远不会跳这么大。

更多细节:传感器是一个测距传感器。它每秒测量10次距离。假设t=1.0s时的距离为10厘米,并将其写入内存。读者读取的共享内存显示为10厘米。现在,如果在读取器读取或处理数据时,实际距离发生变化,则为10.1厘米,或者因为距离永远不会大幅跳跃。在下一次轮询中,读取器将读取10.1cm的距离(假设对象是静止的)。这样,我的编写器线程可以尽可能快地写入,而无需等待互斥锁解锁。

我的推理有缺陷吗?我能想象的唯一问题是,如果我的编写器和读取器线程试图同时访问内存。但是,调度程序应该在指令之间切换,对吧?也就是说,它只是伪并行处理,对吗?这意味着它们不能同时访问内存,对吗?

我不知道这个答案是否应该是评论,如果是,请告诉我。。。

您可以尝试实现一个循环缓冲区。通过这种方式,编写器有一个指针,它只是在缓冲区中旋转并继续写入。读者也有同样的东西,只需要在作者的后面。

这意味着,当编写器写入一些值时,它必须增加变量,该变量说明有多少数据可用。当读者阅读一个样本时,必须减少这个变量。这些操作必须锁定在互斥对象中。尽管i++和i-是原子操作,但在多核系统上,这仍然会造成麻烦,我发现这是一条艰难的道路。

所以,是的,你确实需要互斥,但因为它只需要一个变量,所以它不会让你的整个程序太慢。。。

在您的情况下,问题是读取器可以在写入变量的同时读取,问题只是写入,所以我建议您使用原子操作进行写入,这样您就不需要互斥了。如果数据是对齐的(请参阅Linux内核中的读写原子操作实现),则读取是原子的,我不确定写入操作,但可能不是,所以让我们来做些什么:

在C++中,STL提供了一些材料来保证操作是原子操作,请参见:http://en.cppreference.com/w/c/atomic在C中,我发现了标准中定义的类型sig_atomic_thttp://www.gnu.org/software/libc/manual/html_node/Atomic-Types.html为读取和写入提供原子操作,它应该在没有互斥的情况下完成任务。

如果使用抢占任务(例如中断),这取决于您的实现。读取线程可能正在读取值,并且在读取期间被写入线程中断。在你的情况下,我假设这个值只是一个整数,因此它不是那么重要。只要确保在读取线程中每次执行只读取一次数据(请参阅原子操作)。如果该值大于一个寄存器值。您可以通过使用队列和多级缓冲区来避免互斥。但这会增加你的内存使用量。在您的情况下:如果您的数据大于一个整数,我建议使用三元组缓冲内存。在这种情况下,您拥有三个缓冲区,值被写入第一个缓冲区中,完成后,缓冲区与第二个缓冲区切换,而您的读取线程可以读取第三个。

最新更新