我是使用线程的新手,并且已经阅读了很多关于如何共享数据和保护数据的信息。但是我还没有真正掌握何时需要使用互斥锁和锁来保护数据。
以下是我将要解决的问题的说明。需要注意的重要一点是,它将具有时间关键性,因此我需要尽可能减少开销。
我有两个固定大小的双数组。
第一个数组将为后续计算提供数据。 线程将从中读取值,但永远不会被修改。一个 任何线程都可以在某个时间读取元素。
第二个数组将用于存储计算结果
由线程执行。这个数组的元素只会是
由一个线程更新,并且可能只在结果值
时更新一次
被写入。
我的问题呢?
每次从只读数组访问数据时,是否真的需要在线程中使用互斥锁?如果是这样,你能解释一下原因吗?
我是否需要在写入结果数组时在线程中使用互斥锁,即使这是唯一写入此元素的线程?
我应该使用原子数据类型吗?如果我这样做会有很长时间吗?
这类问题的答案似乎很多 - 不,如果您的变量是对齐的,则不需要互斥锁。我这个例子中的数组元素是否会对齐,或者是否有某种方法可以确保它们是?
代码将在64位linux上实现。我打算使用Boost库进行多线程处理。
感谢所有回复和评论 我一直在仔细研究这个问题并在网上看了好几天,一旦发布了答案,清楚的解释就会在几秒钟内回来。有一个“已接受的答案”,但所有的答案和评论都同样有用。再次感谢
答案 0 :(得分:7)
- 每次从只读数组访问数据时,是否真的需要在线程中使用互斥锁?如果可以,你能解释一下原因吗?
醇>
没有。由于数据永远不会被修改,因此不会出现同步问题。
- 在写入结果数组时,是否需要在线程中使用互斥锁,即使这将是唯一写入此元素的线程?
醇>
取决于
在任何情况下,请注意不要通过不同的线程写入相邻的内存位置。这可能会破坏性能。请参阅“虚假共享”。考虑到,你可能没有很多内核,因此没有很多线程,你说写只做一次,但这可能不是一个重大问题。
- 我应该使用原子数据类型吗?如果我这样做会有很长时间吗?
醇>
如果使用锁(互斥锁),则不需要原子变量(并且它们确实存在开销)。如果不需要同步,则不需要原子变量。如果需要同步,则可以使用原子变量来避免某些情况下的锁定。在哪些情况下你可以使用原子而不是锁...更复杂,我认为超出了这个问题的范围。
鉴于你在评论中对你的情况的描述,似乎根本不需要同步,因此没有原子也没有锁。
- ...这个示例中的数组元素是否会对齐,或者是否有某种方法可以确保它们是?
醇>
正如Arvid所指出的,您可以使用c ++ 11中引入的alginas关键字请求特定对齐。在预C ++ 11中,您可以使用编译器特定的扩展:https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Variable-Attributes.html
答案 1 :(得分:3)
在给出的两个条件下,不需要互斥锁。请记住每个使用互斥锁(或任何同步构造)都是性能开销。因此,您希望尽可能地避免使用它们(当然,不要损害正确的代码)。
没有。由于线程只读取数组,因此不需要互斥锁。
没有。由于每个线程只写入不同的内存位置,因此不可能出现竞争条件。
没有。这里不需要对对象进行原子访问。实际上,使用原子对象可能会对性能产生负面影响,因为它会阻止优化可能性,例如重新排序操作。
答案 2 :(得分:1)
您需要使用锁定的唯一时间是在共享资源上修改数据。例如,如果某些线程用于写入数据而某些线程用于读取数据(在两种情况下都来自同一资源),那么在写入完成时只需要锁定。这是为了防止被称为" race"。
当您制作操作共享资源上的数据的程序时,谷歌上有很好的竞赛信息。
答案 3 :(得分:0)
你走在正确的轨道上。
1)对于第一个数组(只读),您不需要使用互斥锁。由于线程只是读取不改变数据,因此线程无法破坏另一个线程的数据
2)我对这个问题感到有些困惑。如果您知道线程1只会将一个元素写入数组插槽1而线程2只会写入数组插槽2,那么您不需要互斥锁。但是我不确定你是如何实现这个属性的。如果我的上述陈述对您的情况不正确,您肯定需要互斥锁。
3)鉴于原子的定义:
原子类型是封装一个值的类型,该值的访问保证不会导致数据争用,并且可用于同步不同线程之间的内存访问。
注意,互斥锁是原子的意味着只需要一条汇编指令来获取/释放锁。如果需要2个汇编指令来获取/释放锁,则锁将不是线程安全的。例如,如果线程1尝试获取锁并且切换到线程2,则线程2将获取锁。
使用原子数据类型可以减少开销,但不会显着。
4)我不确定你怎么能保证你的变量是排列的。由于线程可以在程序中的任何时刻切换(您的操作系统确定线程何时切换)
希望这有帮助