2012-04-17 58 views
3

我有一个类型为double的共享变量。这个变量将被两个线程访问。一个线程永远只写变量,而另一个线程永远只读变量。共享变量的争用条件

我还有比赛条件吗?如果是的话,C++中有没有一种“简单”的方式来实现原子访问?如果读取数比写入数多得多,我如何有效地实现它?我是否需要将变量标记为volatile

编辑:确定“读取器”线程周期性地在批量数据上工作,并且新值的传播不是时间敏感的。我可以声明写入器线程将写入的另一个临时变量,而不是实现复杂的互锁,而我没有很好的方法来测试。然后,当阅读器完成一个批处理时,它可以将临时值自动传播到实际变量。那会是无竞赛条件的吗?

+0

@Jesse:许多编译器不具有该头的工作实现呢。 – 2012-04-17 22:38:19

+0

一个简单的互斥体可以在这里工作,以确保您正在阅读良好的价值。当写入线程正在使用变量时,互斥锁将阻止对变量的访问,并在读取线程完成工作时解锁它。在unix和windows上,实现都非常简单。 – Chris911 2012-04-17 22:38:47

+0

@ Chris911:互斥锁不必要的昂贵,可能会阻塞一个线程。有一个可以等待的实现。 – 2012-04-17 22:45:22

回答

8

是的,存在竞态条件,因为double变量在大多数处理器上不是原子的。

使用3个双打(可能是一个有额外填充的数组,以避免错误的共享导致性能下降)。

一个由读者拥有,一个由作者拥有,一个正在交出。要写入:写入写入插槽,然后用写入插槽的指针/索引与切换插槽的索引原子交换(例如用InterlockedExchange)。由于索引是指针大小或更小,只要变量正确对齐,原子交换就很容易。如果碰巧你的平台提供了带有和没有记忆障碍的互锁交换,那么使用它。

要读取:将读取插槽的指针/索引与切换变量的索引进行原子交换。然后读取读取插槽。

你实际上也应该包括一个版本号,因为读线程将倾向于在最新和前一个槽之间反弹。阅读时,请在交换前后读取,然后使用版本较高的版本。

或者,在C++ 11中,只需使用std::atomic

警告:以上仅适用于单个作者/单个读者(在此问题中的特定情况)。如果您有多个,请考虑读写器锁定或类似的保护对变量的所有访问。原始类型的

+0

请参阅编辑。 – 2012-04-17 22:47:01

+0

这会如何影响性能?我正在谈论的是每秒发生数百次的读取,而写入可能会在一个小时内发生一次,即使完全一样。 – 2012-04-17 23:05:41

+0

@Jakub:每秒数百次都不算什么,你甚至不用担心表现。如果它每秒数十万次,那么我们应该关注这个问题,我认为我的建议仍然是最快的。 – 2012-04-17 23:37:18

0

你可能想看看这个,讨论的读/写:

Are C++ Reads and Writes of an int Atomic?

+1

'double'的帮助如何,它通常与'int'的大小不一样,并且具有不同的对齐要求? – 2012-04-17 22:36:49

+0

我认为我想摆脱那个线程的关键是读写确实取决于您使用的系统的底层架构(并且我认为如果正确对齐,某些多字节x64指令是原子的),则编译器(即http://msdn.microsoft.com/en-us/library/aa290049%28VS.71%29.aspx)等等。正如你所指出的那样,假设它不是原子的并且从那里开始更安全。在本人的回应中,我可以做得更清楚些。 – 2012-04-17 22:58:34