是否同时覆盖具有相同值的变量?

时间:2010-08-17 13:39:56

标签: c++ multithreading concurrency synchronization

我遇到以下情况(由代码中的缺陷引起):

有一个基本类型的共享变量(让它为int),它在程序启动时从严格的一个线程到值N初始化(让它为0)。然后(在变量初始化之后严格)在程序运行期间启动各种线程,并且它们以某种随机顺序读取该变量或用相同的值覆盖它N (本例中为0)。访问变量没有同步。

这种情况会导致程序出现意外行为吗?

6 个答案:

答案 0 :(得分:5)

根据标准,这是非常不可能的,但并非不可能。

没有任何说明整数的基础表示是什么,标准不指定如何加载值。

我可以设想,无论多么奇怪,一个实现,其中0的基础位模式是10101010,并且该架构仅支持通过将其移位8个周期将数据加载到存储器中,但是将其作为单个单元读取一个周期。

如果另一个线程在移位位模式时读取该值(例如000000010000001000000101等等,则会出现问题。

任何人设计这样一个奇怪的架构的可能性都接近于零,可以忽略不计。但不幸的是,它不是零。我想要了解的是,在标准合规方面,你根本不应该依赖假设。

请在投票给我之前,请随意引用标准中指出这是不可能的部分: - )

答案 1 :(得分:4)

由于C ++目前没有标准的并发模型,它完全取决于您的线程实现及其提供的任何保证。然而,在一般情况下,它几乎肯定是不安全的,因为可能会出现撕裂的读数。可能存在特定情况,它将“起作用”或至少“看起来有效”。

在C ++ 0x(具有标准并发模型)中,您的场景将正式导致未定义的行为。在C ++ 0x最终委员会草案§1.10中有一个冗长,详细,难以阅读的并发模型规范,但它基本上归结为:

  

如果其中一个修改了内存位置而另一个访问或修改了相同的内存位置(§1.10/ 3),则两个表达式评估会发生冲突。

     

程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会在另一个之前发生。任何此类数据竞争都会导致未定义的行为(§1.10/ 14)。

您的表达式评估明显冲突,因为它们修改并读取相同的内存位置,并且由于该对象不是原子的,并且使用锁未同步访问,因此您有未定义的行为。

答案 2 :(得分:2)

没有。当然,如果其中一个线程稍后尝试更改该值,您最终可能会遇到数据竞争。你最终会得到一点缓存争用,但我怀疑这会产生明显的影响。

答案 3 :(得分:1)

你不能真正依赖它。对于原始类型,你应该没问题,如果操作是原子的(例如在大多数平台上都是正确对齐的int),那么写和读取不同的值是安全的(请注意,这意味着像“x = 5;”, not “x + = 5;”这绝不是原子的,也不是线程安全的。)

对于非原始类型,即使它的值相同,所有的注意都是关闭的,因为可能存在一个不安全的复制构造函数(比如分配内存)。

答案 4 :(得分:1)

是的,在这种情况下可能会发生意外行为。考虑变量的初始值不为0的情况。一个线程可以将该集启动为0,另一个线程可以看到只设置了一些字节的变量。

对于类型int,这是不太可能的,因为大多数处理器将具有字大小值的原子分配。但是,一旦你达到8位数值(在某些平台上long)或大型结构,这就开始成为一个问题。

答案 5 :(得分:0)

如果没有其他线程(这包括主线程)可以将0的值更改为其他任何内容(比如1),而这些线程正在初始化,那么它就不会有问题。但是,如果任何其他线程有可能在启动阶段更改该值,则可能会出现问题。你正在玩一个危险的游戏,我建议你在阅读之前锁定。