读写竞争条件是否会改变同时读取和写入的数据?

时间:2014-02-05 12:40:14

标签: c++ multithreading race-condition

例如,如果我有以下代码:

SomeType obj;  

void GUIThread()  
{  
    ...  
    while (true)  
        // Read and print the content of obj  
    ...  
}  

void workerThread()  
{  
    ...  
    // Do some calculation and write the result into obj  
    ...  
}  

出于性能原因,我无法在obj上使用互斥锁或任何类似内容,而且我不需要GUIThread打印(obj存储正在运行的内容的准确性workerThread的计算结果,可以视为“运行总计”。GUIThread需要做的只是显示在workerThread中完成计算的大致进度。我想确定的是GUIThreadworkerThread之间的读写竞争条件不会改变obj中存储的数据,也不会导致程序崩溃。真的吗?

P.S。 SomeType包含内置整数类型和std::bitset<T>,但只有前者可以同时访问。 bitset保持GUIThread不受影响。

P.P.S。也许这有点偏离主题...但我认为我可以将运行结果存储在workerThread的缓存中,并且只更新实际受保护的(通过原子性或互斥锁定或其他)每个相对很长一段时间。为实现这一目标,我需要确保如果以下代码按预期工作:

struct SomeOtherType  
{  
    int a, b, c, d;  // And other primitive types  
}  

std::atomic<SomeOtherType> data;  // Will this work?  

我想知道这是否可以保护SomeOtherType,我认为它可以,因为SomeOtherType中只有原始类型。

3 个答案:

答案 0 :(得分:4)

你所说的是“我正在编写具有未定义行为的代码。无论如何,它会做我想要的吗?”。唯一的答案是除了可能的编译器编写者之外没有人知道。

看起来您的代码可能会执行您想要的操作,因为它似乎是实现编译器的最简单方法。但它可能会决定你的循环之外的任何东西都不能写入该变量,因为如果它确实存在,那将是数据竞争,因此标准没有说明它将做什么,然后决定用变量替换变量程序的一部分,因为它无法更改并重新使用该内存来存储另一个指针的值,因此您的其他线程将一个完全未连接的值设置为null并在一小时后崩溃您的程序。当然不太可能,但我可以看到它是如何发生的。

未定义的行为就是 - 未定义。它确实可以做任何事情。有些事情显然比其他事情更有可能,但是你真的想冒险格式化用户的磁盘,因为你认为“未定义”并不适用于你的程序,不管风险有多小?

答案 1 :(得分:1)

据我了解,你的结构看起来像这样

struct Data {
    int one;
    int two;
    std::bitset<SomeType> three;
}

如果您不想使用任何类型的锁,可以尝试将共享指针交换到此结构。检查您的编译器是否支持它,这是一个新功能。

std::shared_ptr<Data> dataPointer;

void GUIThread()  
{  
    ...  
    while (true)  {
       auto ptr = std::atomic_load(&dataPointer);
       // Read and print the content of *ptr
    ...  
}  

void workerThread()  
{  
    ...  
   // Do some calculation
   auto newPtr = std::make_shared<Data>();
   // make the new result visible to the gui thread
   std::atomic_store(&dataPointer, newPtr);
}

答案 2 :(得分:0)

严重依赖于共享数据类型。 在复杂数据类型中可能发生的是,在写入操作期间,对象短暂地变为无效(例如,分配新的内部缓冲区)。

对于简单的数据类型,例如整数,浮点数等,代码不会在大多数情况下崩溃但没有保证!。这些应该声明为volatile,以避免编译器将它们缓存在寄存器中。强制使用内存屏障可以提高结果的准确性。

如果你只是检查一个布尔标志或类似的东西,你就可以逃脱。

如果obj是线程安全的,那么你也可以。

我的建议,如果有疑问,请使用低开销锁,例如自旋锁。