我在多处理器机器上用C++写了一个boolean类型,变量一开始是true,然后有几个线程,任何一个或多个线程都可能把它写为false。
同时,这些线程也会读取这个变量来检查它的状态,我不关心读取这个变量是否与任何写操作同步,它们都发生在代码中的不同位置,并且不管它是在任何特定的写操作之前还是之后,现在,我需要为这个布尔值加锁吗?
我需要锁的唯一原因是,在非常低的级别上,内存可能会被两个竞争性的写操作破坏。例如,如果处理器A上的汇编指令向表示布尔值的字节写入0,同时处理器B也在做同样的事情......而不是写入0,内存最终得到值22或其他值。这可能会把事情搞砸。
因此,一般来说,如果proc A正在向内存位置写入3,而proc B正在写入7,并且没有同步,那么我是否可以保证最终得到的结果至少是3或7?
编辑:
谢谢大家的评论。更多信息:程序中当然有同步。总结一下,有问题的标志是告诉某个内存池是否"脏"(需要压缩)。因此,任何线程都可以决定将此标志设置为false(表示池已脏)。例如,从内存池中释放内存会使内存变脏。然后,任何线程都可以读取这个标志并设置另一个标志来表示需要清理--这个检查是在从内存池分配内存时完成的,如果内存不足,就会发出清理信号。在迭代之间的主临界区的某个地方,每个线程都会去寻找更多的数据来处理,我会让线程检查第二个标志,并做一些适当的事情来确保:所有其它线程完成其当前迭代,一个线程清除存储器,将第一标志设置回真(如在Pool中不是脏的),将第二标志设置回假,然后再次释放所有线程。
所以我觉得我不需要锁,因为:锁可以确保写操作不会与另一个写操作或读操作同时发生。但谁在乎呢,只要硬件不出故障,最坏的情况是读操作随机发生在写操作之前或之后--这与我用锁保护它时会发生的情况相同,那时我们 * 真的 * 确定它发生在写操作之前或之后......
我想同样的论点也适用于我上面提到的第二面旗帜。
6条答案
按热度按时间c0vxltue1#
在大多数商用硬件上,单个字的读/写是原子的,因此,对同一内存位置的两个(或更多)竞争性写(和读)不会损坏值。这里重要的是CPU之间的缓存一致性。
同样,在商用硬件上,你可能只需要标记一个布尔变量
volatile
(顺便说一句,它已经被声明对并发编程毫无用处),以防止编译器将其优化到寄存器中,**但前提是你真的不关心写操作的顺序。让我用一个检查表重复一遍:
如果你有三个强有力的"是"答案,你 * 可能 * 不保护那个标志。仍然可以考虑在读取变量之前插入acquire memory barrier,并在写入变量之前插入release memory barrier。我的建议是重新考虑设计,并制定清晰的同步线程间通信和事件序列。
希望这个有用。
xsuvu9jc2#
如果只检查变量的状态并将其设置为false,那么除了一些线程可能晚一点才看到变量已经设置为false之外,没有什么可担心的。(这可以通过使用“volatile”关键字在某种程度上克服。)则两个线程可以将其设置为假,这是没有问题的,因为变量在一个方向上被设置为一个值,假设布尔写内存位置不能保证是原子的,有什么坏处呢,它们最终写的值是一样的。
不过,在以下情况下,您仍必须使用锁定方法:
htrmnn0y3#
这是一个简单的方法来打破东西。与布尔,你可能是好的,但大多数时候,没有提供任何保证。
您有两种选择:使用互斥锁,或者使用原子原语。原子原语将利用硬件指令以线程安全的方式进行测试和设置操作,而不需要实际的互斥锁,并且是一种轻量级的解决方案。GNU编译器通过特定于体系结构的扩展提供对原子操作的访问。还有可移植的原子操作库; GlibC库提供了原子操作,如果原子原语不可用,则可以退回到使用互斥体,尽管它是一个相当繁重的库,还具有许多其他特性。
Boost.Atomic库为C抽象了原子操作;从它的名称来看,它似乎打算被合并到Boost C库集合中,但还没有实现。
wmvff8tz4#
你问的是两件事。
首先,你问的是布尔赋值的原子性。不,没有人能保证布尔赋值是原子操作。实际上,它通常是原子操作,但你不应该依赖于此。一些奇怪的体系结构可能会在许多机器指令中实现布尔赋值...
其次,你会问到由于并行写入而导致的数据损坏--实际上,从CPU到内存的传输是通过总线完成的,总线几乎总是包含比你正在处理的原语类型更多的位。因此,这种损坏可能会发生在非常奇怪的体系结构中,或者在处理大量数据时(系统本身不支持)。实际上,您通常会得到3或7。但同样,您不能依赖它。
要总结这一点-你需要一把锁。
jtoj6r0c5#
在大多数商用硬件上,单个字的读和写不是原子操作;记住这里有虚拟内存机器,这些操作中的任何一个都可能导致页面错误。
更一般地说,你可能会发现把互斥锁作为一种日常事务来使用要比挠头想一想这次是否不用互斥锁更容易、更快。在不必要的地方添加互斥锁不会导致bug;把一个人留在了可能的地方。
cgvd09ve6#
对于bool,答案通常是否定的,不需要互斥锁,但是(正如Michael E所指出的)任何事情都可能发生,所以在做出这样的决定之前,你可能需要对你的arch有更多的了解。代码可能仍然需要围绕与布尔值相关联的整个逻辑的锁定,尤其是如果在例程的逻辑过程中不止一次地读取布尔值。
我读了一些很棒的博客,以保持我的多线程脚趾:
Link
http://herbsutter.com/2009/04/20/effective-concurrency-use-thread-pools-correctly-keep-tasks-short-and-nonblocking/
此致,