我有一个传感器正在以n Hz(例如10Hz =每秒10次)的线程将数据写入共享存储器。一个单独的线程正在读取此数据并使用它来获取某些结果。读者线程的频率是不同的。它可以更慢,例如每秒8次或更快,例如每秒15次,具体取决于计算的内容。读者线程只是从共享内存中读取数据。它不会修改数据(仅处理它以获得某些结果)并且不会向共享内存写入任何内容。整个过程非常巧妙。我不关心同步,因为读者只需在需要时读取共享内存中的内容(它会轮询数据)。如果在两次读取之间,共享内存的内容发生变化,则阅读器将使用新数据。如果在两次读取之间共享内存的内容没有改变(如果读取器比写入器快得多),则读者只使用共享内存中的任何数据。
现在,我的同事告诉我使用互斥锁同步访问共享内存,但我不同意。原因是,如果我使用互斥锁来控制访问,写入共享内存的写入程序的频率将会有所降低(当读取器线程锁定互斥锁并写入时)。在未来,我们将有更多的读者线程,我担心写入器线程可以写入共享内存的频率将进一步降低,因为将有两个线程竞争互斥锁。
我了解竞争条件等但我觉得竞争条件和SO以及其他网站上提供的大量示例都考虑了与我不同的情况:两个线程正在读取和处理银行余额并且一个线程更慢或更快的示例在阅读和平衡金额最终错误...导致2000美元而不是1000美元。但是,就我而言,“银行余额” - 要共享的数据是由传感器生成的。值的任何变化都是由于物理原因造成的,并且要共享的数据值永远不会跳得太大。
更多细节:传感器是距离测量传感器。它在一秒钟内测量距离10次。假设t = 1.0s处的距离是10cm并且它被写入记忆。读者读取10cm的共享内存。现在,如果实际距离恰好在读者正在阅读或处理数据时发生变化,那么它将是10.1厘米,或者因为距离永远不会大幅跳跃。在下一次轮询中,读者将读取10.1cm的距离(假设对象是静止的。)这样,我的编写器线程可以尽可能快地写入,而无需等待互斥锁被解锁。
我的推理是否有缺陷?我能想到的唯一问题是,如果我的编写器和读取器线程试图在同一时间访问内存。但是,调度程序应该在指令之间切换,对吧?也就是说,它只是伪并行处理,对吗?这意味着他们两个都无法同时访问内存,对吗?
答案 0 :(得分:3)
我不知道这个答案是否应该是评论,如果是,请告诉我......
您可以尝试实现循环缓冲区。这样,编写器就有一个指针,它只是通过缓冲区旋转并继续写入。读者也一样,只需要在作家之后“落后”。
这意味着当编写器写入某些值时,它必须增加表示可用数据量的变量。当读者读取样本时,必须减少此变量。这些操作必须锁定在互斥锁中。虽然i ++和i--是原子操作,但在多核系统上,这仍然会带来麻烦,我发现这很难。
所以是的,你确实需要互斥锁,但因为它只需要一个变量,所以它不会减慢整个程序的速度......
答案 1 :(得分:1)
如果您使用的是preemtive任务(例如中断),则取决于您的实施。读取线程可能正在读取值并且在读取期间被写入线程中断。在你的情况下,我假设该值只是一个整数,因此它不是那么批评。只需确保在阅读线程中每次执行只读取一次数据(参见原子操作)。如果该值大于一个寄存器值。您可以通过使用队列和多级缓冲区来避免使用互斥锁。但这会增加你的内存使用量。在你的情况下:如果你的数据大于一个整数,我会建议使用tripple缓冲内存。在这种情况下,您拥有三个缓冲区,该值在第一个中写入,在完成之后,缓冲区与seconed一起切换,而您的读取线程可以读取第三个缓冲区。
答案 2 :(得分:1)
您的情况下的问题是读者可以在写入变量的同时读取,问题只是写入,所以我建议您使用原子操作进行此写操作,这样您就不会需要互斥锁。如果数据是对齐的,则读取是原子的(参见Read and Write atomic operation implementation in the Linux Kernel),我不确定写入操作,但也许他们不是这样,让我们能做什么:< / p>
在C ++中,STL提供了一些材料来保证操作是原子的:http://en.cppreference.com/w/c/atomic 在C中,我发现在标准http://www.gnu.org/software/libc/manual/html_node/Atomic-Types.html中定义了这种类型的sig_atomic_t,它保证了读取和写入的原子操作,它应该在没有互斥的情况下完成。