c++ - 整数读取需要临界区保护吗?

标签 c++ winapi concurrency critical-section

我遇到过 C++03 中一些采用这种形式的代码:

struct Foo {
    int a;
    int b;
    CRITICAL_SECTION cs;
}

// DoFoo::Foo foo_;

void DoFoo::Foolish()
{
    if( foo_.a == 4 )
    {
        PerformSomeTask();

        EnterCriticalSection(&foo_.cs);
        foo_.b = 7;
        LeaveCriticalSection(&foo_.cs);
    }
}

foo_.a 的读取是否需要保护?例如:

void DoFoo::Foolish()
{
    EnterCriticalSection(&foo_.cs);
    int a = foo_.a;
    LeaveCriticalSection(&foo_.cs);

    if( a == 4 )
    {
        PerformSomeTask();

        EnterCriticalSection(&foo_.cs);
        foo_.b = 7;
        LeaveCriticalSection(&foo_.cs);
    }
}

如果是,为什么?

请假设整数是 32 位对齐的。平台是 ARM。

最佳答案

技术上是的,但在许多平台上都不是。首先,让我们假设 int 是 32 位(这很常见,但不是通用的)。

32 位 int 的两个字(16 位部分)可能会被单独读取或写入。在某些系统上,如果 int 未正确对齐,它们将被单独读取。

想象一个系统,您只能进行 32 位对齐的 32 位读取和写入(以及 16 位对齐的 16 位读取和写入),以及一个跨越这种边界的 intint 最初为零(即 0x00000000)

一个线程将 0xBAADF00D 写入 int,另一个“同时”读取它。

写入线程首先将0xBAAD写入int的高位字。然后读取器线程读取整个 int(包括高位和低位),得到 0xBAAD0000 —— 这是 int 从未进入的状态故意的!

写入器线程然后写入低位字 0xF00D

如前所述,在某些平台上,所有 32 位读/写都是原子的,因此这不是问题。然而,还有其他问题。

大多数锁定/解锁代码都包含对编译器的指令,以防止跨锁重新排序。如果没有防止重新排序,编译器可以自由地重新排序,只要它在单线程上下文中表现得“好像”它会那样工作。因此,如果您在代码中读取 a,然后读取 b,编译器可以在读取 a 之前读取 b,如此长因为它没有看到在该时间间隔内修改 b 的线程内机会。

因此,您正在阅读的代码可能正在使用这些锁来确保变量的读取按照代码中写入的顺序进行。

其他问题在下面的评论中提出,但我觉得没有能力解决它们:缓存问题和可见性。

关于c++ - 整数读取需要临界区保护吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13650829/

相关文章:

c# - 检测 OpenFileDialog

python - Django 页面点击计数器并发

c++ - 默认(可选)参数作为引用 c++

c++ - Xcode - 如何控制 macbook 的 USB 端口的电源?

c++ - 初始化结构值

winapi - GetPrivateProfileString 奇数

c++ - 如何在另一个进程的内存中搜索字符串?

java - 在两个线程和主程序之间共享一个对象

java - 协调多个并发队列

c++ - 挑战 - Arduino 返回多个数组