【问题标题】:Is x++; threadsafe?是 x++;线程安全?
【发布时间】:2023-03-27 13:08:01
【问题描述】:

如果我像这样在一个线程中更新一个变量: 接收计数器++;

然后从另一个线程我只读取这个变量并将其值写入 GUI。

这样安全吗?或者这条指令是否可以在中间被中断,所以当另一个线程读取它时,receiveCounter 中的值是错误的?它一定是对的,因为 ++ 不是原子的,它是几个指令。

我不关心读取和写入的同步,它只需要递增然后在 GUI 中更新,但这不必彼此直接发生。

我关心的是值不能错。就像 ++ 操作在中间被中断所以读取的值完全关闭。

我需要锁定这个变量吗?我真的不想,因为它经常更新。我可以通过将消息发布到主线程并将值复制到队列来解决这个问题(然后需要锁定,但我不会在每次更新时都这样做)我猜。

不过我还是对上面的问题很感兴趣。

【问题讨论】:

标签: c++ multithreading atomic


【解决方案1】:

如果一个线程更改了变量中的值,而另一个线程读取了该值,并且程序没有同步访问,则存在数据竞争,并且程序的行为未定义。将receiveCounter 的类型更改为std::atomic<int>(假设它以int 开头)

【讨论】:

    【解决方案2】:

    它的核心是读-修改-写操作,它们不是原子的。周围有一些处理器有专门的指令。像 Intel/AMD 内核一样,很常见,它们有一个 INC 指令。

    虽然听起来可能是原子的,但由于它是一条指令,它仍然不是。 x86/x64 指令集与执行引擎的实际实现方式没有太大关系。它执行类似 RISC 的“微操作”,INC 指令被翻译成多个微操作。 可以通过指令上的 LOCK 前缀使其成为原子。但编译器不会发出它,除非他们知道需要原子更新。

    因此,您需要明确说明。 C++11 std::atomic 标准添加是一个好方法。您的操作系统或编译器将具有它的内在函数,通常命名为“Interlocked”或“__built_in”。

    【讨论】:

    • 我理解这个问题,因为只有读取部分和写入部分需要是原子的。不是整个读-修改-写。这不应该是对齐和数据大小的问题。
    【解决方案3】:

    简单的回答:不。

    因为i++;i = i + 1; 相同,它包含加载、数学运算和保存值。所以它通常不是原子的。
    然而,真正执行的操作取决于 CPU 的指令集,并且可能是原子的,具体取决于 CPU 架构。但默认仍然不是原子的。

    【讨论】:

    • 正确答案,错误推理。它可以在某些处理器上以单条指令实现。但是,由于问题中未指定处理器,并且在 C++ 标准中未将操作指定为原子操作,因此它不是线程安全的。
    【解决方案4】:

    一般来说,它不是线程安全的,因为 ++ 运算符由一个 read 和一个 write 组成,这对不是原子的,可以在两者之间中断。

    然后,它可能还取决于语言/编译器/架构,但在典型情况下,增量操作可能不是线程安全的。

    (已编辑)

    至于 readwrite 操作本身,只要你不是在 64 位模式下,它们应该是原子的,所以如果你不在乎关于其他线程的值错误 1,您的情况可能没问题。

    【讨论】:

    • 不仅如此,有时候如果结果不正确也可以ok。例如,如果 GUI 线程正在轮询变量,那么在几分之一秒内有错误的值可能是可以的。
    • 确实是@JoachimPileborg。在进度计数器/进度条等情况下,每秒在计时器上更新一次,所看到的值无论如何总会在某种程度上过时,那么为什么还要为偶尔的打嗝而烦恼呢?在许多处理器上,++ 无论如何都会以单指令 INC 结尾。
    【解决方案5】:

    没有。增量操作不是原子的,因此不是线程安全的。

    在您的情况下,如果您不关心它在特定时间的值(如果您只从另一个线程读取此变量而不尝试写入它),则使用此操作是安全的。它最终会增加receiveCounter 的值,只是你对操作顺序没有任何保证。

    【讨论】:

      【解决方案6】:

      ++ 等价于i=i+1

      ++ 不是atomic operation,所以它的NOT 线程安全。只有基本变量(long 和 double 除外)的读写是原子的,因此是线程安全的。

      【讨论】:

      • 不,i++ 不等于 i = i+1
      • @EJP 你能澄清一下上面的评论吗?
      • “只有基本变量(除了 long 和 double)的读写是原子的”。 C++11 甚至不保证这一点。如果你想要原子性和线程安全,你可以使用std::atomic,没有捷径。
      猜你喜欢
      • 2017-10-04
      • 1970-01-01
      • 2011-08-13
      • 2010-12-10
      • 2012-08-21
      相关资源
      最近更新 更多