我需要用锁来保护这个变量吗?

时间:2010-07-12 00:32:07

标签: c++ synchronization

所以我在多处理器机器上有一个C ++的布尔类型。变量的生命开始为真,然后有一些线程,其中任何一个或多个都可能将其写为假。

同时,这些线程也可以读取此变量以检查其状态。我不关心如果读取此变量与任何写入同步,它们每个都发生在代码中的不同位置,并且无论是在任何特定写入之前还是之后都无关紧要。现在,我需要锁定这个布尔值吗?

我需要锁定的唯一方法是,如果处于非常低的水平,内存可能会被两次竞争写入损坏。例如,如果处理器A上的汇编指令在处理器B执行相同操作的同时将0写入表示布尔值的字节...而不是写入0,则内存最终为值22或一些东西。这可能搞砸了。

所以,一般来说,如果proc A正在写3到一个内存位置,而proc B正在写7,没有同步,我保证最终得到3或7?还是容易打破记忆?

编辑:

感谢评论家伙。更多信息:当然程序中有同步。总而言之,有问题的标志是告知某个内存池是否“脏”(需要压缩)。因此,任何线程都可以决定将此标志设置为false(意味着池是脏的)。例如,从池中释放内存会使其变脏。然后,任何线程都可以读取此标志并设置另一个标志以指示需要清理 - 当从池中分配内存时执行此检查,如果内存不足则会发出清除信号。在迭代之间的主要关键部分的某处,每个线程都要查找更多要处理的数据,我将让线程检查第二个标志,并做一些适当的事情来确保:所有其他的theads完成当前的迭代,一个线程清理内存,将第一个标志设置为true(如池中不脏),将第二个标志设置为false,然后再次释放所有线程。

所以我认为我不需要锁定,因为:锁定会确保写入不会与另一次写入或读取同时发生。但谁在乎呢,只要硬件没有让我失望,最糟糕的情况是读取在写入之前或之后随机发生 - 如果我用锁保护它会发生同样的事情,就在那时我们确实确定它是在...之前或之后......

我认为同样的论点适用于我上面提到的第二个标志。

6 个答案:

答案 0 :(得分:10)

在大多数商品硬件上,单个字读/写都是原子的,因此,对同一个内存位置的两次(或多次)竞争写入(和读取)不会破坏该值。这里重要的是CPU之间的缓存一致性

同样,在商品硬件上,您可能只需标记单个布尔变量volatile(已被声明为并发编程btw无用),以防止编译器将其优化到寄存器中,但是只有你真的不关心写作的顺序

让我用一个核对清单重复这个:

  • 你准备好了吗会失去对布尔值的一些更新吗?
  • 您确定在源代码中的布尔值翻转之前没有其他内存更新,但可能会在之后重新排序,因为翻转会搞砸了吗?
  • 您确定不关心申请中的活动顺序吗?

如果您有三个强烈的“是”答案,那么可能会躲避不保护该旗帜。仍然可以考虑在读取变量之前插入获取内存屏障,并在编写之前插入释放内存屏障。我的建议是重新思考设计,并明确同步的线程间通信和事件排序。

希望这有帮助。

答案 1 :(得分:8)

如果只检查变量的状态并在某个方向上将其设置为false,那么除了某些线程可能有点迟,看到变量已经设置为false之外,没什么好担心的。 (这可以通过使用'volatile'关键字在某种程度上克服。)然后两个线程可以将其设置为false,这没有问题,因为变量在一个方向上设置为单个值。假设对内存位置的布尔写入不保证是原子的,有什么危害?他们都写的最终值是相同的。

但是,如果出现以下情况,则必须使用锁定方法:

  • 值设置不仅仅是单向:您将其设置为false,然后再设置为true,然后再设置为false等。
  • 您对线程将值设置为false的信息采取了一些相关操作。因为显然可能有两位获胜者。

答案 2 :(得分:3)

这是打破事情的简单方法。使用布尔值,您可能在大多数情况下都可以,但无法保证。

您有两种选择:使用互斥锁(锁定)或使用原子基元。原子原语将利用硬件指令以线程安全的方式进行测试和设置操作,而不需要实际的互斥锁,并且是一种更轻量级的解决方案。 GNU编译器通过特定于体系结构的扩展提供对原子操作的访问。还有便携式原子操作库浮动;如果原子基元不可用,Glib C库提供的原子操作可以回退到使用互斥锁,尽管它是一个包含许多其他功能的相当重的库。

Boost.Atomic库抽象C ++的原子操作;基于它的名称,看起来它的目标是被合并到Boost C ++库集合中,但还没有成功。

答案 3 :(得分:1)

对于bool来说,答案通常是否定的,不需要互斥锁,但(正如Michael E所说)任何事情都可能发生,所以在做出这样的决定之前你可能需要更多地了解你的拱门。另一个注意事项:代码可能仍然需要锁定与bool相关的整体逻辑,特别是如果bool在例程逻辑过程中被多次读取。

我读过一些很棒的博客,让我保持多线程的脚趾:

http://blogs.msdn.com/b/nativeconcurrency/

http://herbsutter.com/2009/04/20/effective-concurrency-use-thread-pools-correctly-keep-tasks-short-and-nonblocking/

最好的问候,

答案 4 :(得分:1)

你问的是两件事。

首先你要问的是布尔分配的原子性。不能保证布尔赋值将是原子操作。在实践中它通常是,但你不应该依赖于此。一些奇怪的架构可能会在许多机器指令中实现bool赋值......

辅助,你要问的是由于并行写入而导致的数据损坏 - 实际上,从CPU到内存的传输是通过总线完成的,总线几乎总是包含比你正在处理的原始类型更多的位。因此,对于非常奇怪的体系结构或处理大数字(本身不支持系统)时,可能会发生此类损坏。在实践中,你通常会得到3或7.但同样,你不能依赖它。

总结一下 - 你需要一把锁。

答案 5 :(得分:1)