会读取的种族条件会同时改变读取和书写的数据

Will a read-write race condition alter the data read and written simultaneously?

本文关键字:读取 数据 改变 书写 条件      更新时间:2023-10-16

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

SomeType obj;  
void GUIThread()  
{  
    ...  
    while (true)  
        // Read and print the content of obj  
    ...  
}  
void workerThread()  
{  
    ...  
    // Do some calculation and write the result into obj  
    ...  
}  

出于绩效原因,我无力使用MUTEX或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中只有原始类型。

您在说的是"我正在编写不确定行为的代码。它会做我想做的事吗?"。唯一的答案是没有人知道,除了编译器作家。

似乎您的代码可能会做您想要的事情,因为这似乎是实现编译器的最简单方法。但是它很可能会决定循环之外的任何内容都可以写入该变量,因为如果这样做,那将是一场数据竞赛,因此,标准对其将要做什么没有说明该程序的一部分由于无法更改并重复使用该内存以存储另一个指针的值,因此您的其他线程将为NULL设置完全无连接的值,并在一个小时后将您的程序崩溃。当然,这不太可能,但我可以看到它可能是如何发生的。

不确定的行为就是 - 未定义。从字面上看,它可以做任何事情。有些事情显然比其他事情更有可能,但是您真的想冒险格式化用户的磁盘,因为您认为"未定义"不适用于您的程序,无论风险似乎有多小吗?

据我了解,您的结构看起来像

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);
}

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

对于简单的数据类型,例如整数,浮点等。代码在大多数情况下不会崩溃,但请保证!。应将其声明为volatile,以避免编译器缓存在寄存器中。强迫记忆障碍将提高您的结果准确性。

如果您只是在检查布尔国旗或类似的东西,则可以逃脱。

如果OBJ是线程安全的,那么您也可以。

我的建议,如有疑问,请使用诸如Spinlocks之类的低顶锁。