C++ 比较是原子操作吗?

C++ 比较是原子操作吗?,c++,multithreading,mutex,atomic,operation,C++,Multithreading,Mutex,Atomic,Operation,下面的比较是原子行为吗?也就是说,它可以简化为单个CPU指令吗 char flag = 2; for(;;) { if (!flag) // <-- this break; // sleep } char标志=2; 对于(;;) { 如果(!标志)//< P>不,C++不保证任何操作都是原子的。你的问题代码可能会被编译成一个从内存到寄存器的负载,它本身可能会接受多个指令,然后是一个测试。 < P>没有任何C++操作被保证是原子操作。C++中的< /P>

下面的比较是原子行为吗?也就是说,它可以简化为单个CPU指令吗

char flag = 2;

for(;;)
{
    if (!flag) // <-- this
        break;

    // sleep
}
char标志=2;
对于(;;)
{

如果(!标志)//< P>不,C++不保证任何操作都是原子的。你的问题代码可能会被编译成一个从内存到寄存器的负载,它本身可能会接受多个指令,然后是一个测试。

< P>没有任何C++操作被保证是原子操作。C++中的< /P> < P>没有任何保证是原子的。< /P>登记符>比较不是原子性的,因为它需要多条机器语言指令来完成(从内存加载到寄存器等),也因为内存模型的灵活性,缓存执行测试的线程可能不会立即“看到”另一个线程的结果

如果变量是标记为易失的,但这将是平台特定的,那么您可能是安全的。正如其他人指出的,C++本身对此没有保证。

< P>强> NO> <强> > < /P> 比较包括读取两个数据块以及执行实际比较。数据可以在读取指令和比较指令之间更改,因此它不是原子的


但是,由于您正在比较是否相等,instrinsic(用于x86中的
锁cmp xchg
指令)可能会执行您需要的操作,尽管它将涉及替换数据。

这是错误的方法

您的主线程正在以最快的速度消耗CPU周期,除了等待
标志
达到零外,什么也不做。每次尝试此测试时都会失败,最后一次除外。不要以这种方式进行测试,请使用“join”您的线程对象很可能必须使主线程挂起,直到所有工作线程完成为止


这种方式,不是巧合的,你不会关心测试是否是原子的,因为你根本不需要它。

< P>不。据我所知,C++不能保证原子是什么,什么不是平台特定的。即使你的平台保证了可以进行原子性的比较,也不能保证你的C++编译器会CHO。请遵守指示

然而,在实践中,简单值类型(如char、int、float等)的比较可能是原子性的。但是,您仍然需要了解编译器级别和处理器级别指令的可能重新排序。在这种情况下,这可能不重要,但在一般情况下,它可以并且确实存在。您还需要了解在比较时,分支不是原子的,即使比较是原子的-因此,如果您试图使用标志来规范访问,那么两个线程都可以输入相同的代码块


如果您需要适当的保证,Windows和gcc上有各种各样的保证。

您应该问的问题是“是——原子的”?这就是这里最重要的。当标志达到0时,您需要做一些事情

您不关心这个场景:

1. Main thread reads flag, and it is 1.
2. Worker changes flag with --
3. Main thread doesn't see that flag is actually 0.
因为在1ns内,主线程循环并重试

您确实关心--不是原子的,两个线程同时更改它将跳过递减:

1. Thread A reads flag, flag is 2
2. Thread B reads flag, flag is 2
3. Thread A decrements its copy of flag, 2, and writes to flag, flag is 1
4. Thread B decrements its copy of flag, also 2, and writes to flag, flag is 1.
您丢失了减量。您想使用(&flag,1),它将自动减量标志


最后,围绕睡眠旋转并不是最好的方法。您需要等待一个条件,等待一个。让工作线程在意识到已将标志减至0时提高条件或信号。

我认为,在进行比较之前,必须始终将其读入寄存器,因为您已将指针传递给不,它不是原子的。我想,我不确定。我相信有办法做原子比较和写,虽然(我猜不是C++语言本身)。…这并不意味着如果某个指令可以简化为单个CPU指令,那么它就是原子指令。首先,并非所有体系结构上的所有CPU指令都是原子指令,其次,仅仅因为某个指令可以简化为原子指令并不意味着它会是原子指令。然而,在许多平台上,如果比较结果为零,则存在d指令操作码来执行该操作。这意味着,如果值已经在寄存器中,它确实可以原子地执行。@Kylotan:我认为这无关紧要,您仍然需要将其读入寄存器,然后进行比较。问题不在于比较,而在于中间状态。但是如果您有指向内存的指针(而不是寄存器中的值)那么是的,这是正确的;我已经更新了我的答案。如果该值已经在寄存器中,但你如何保证?你不会。我不是反对这个答案中的“否”部分。我只是说比较并不总是涉及读取两条数据。与任何常数进行比较时,重要的不是数据可能会改变在读取和比较指令之间进行比较,但它可能会在读取时间和对比较结果采取行动的时间之间发生变化。如果读取是原子的,因为在对其采取行动之前,比较不会产生可观察的影响,因此比较是原子的或n没有意义当然,如果读取不是原子的,那么比较也不会。那么我需要更改代码以使其线程安全吗?还是已经是了?@Truncheon术语“线程安全”是毫无意义的-线程安全完全依赖于上下文。但一般来说,任何资源(变量,无论什么)从多个线程访问的代码必须受到某种锁的保护。留给自己的设备,编译器永远不会生成“线程安全”的代码。该死。我应该想到这一点。谢谢。我相信原始代码不会进行繁忙的等待:)
1. Thread A reads flag, flag is 2
2. Thread B reads flag, flag is 2
3. Thread A decrements its copy of flag, 2, and writes to flag, flag is 1
4. Thread B decrements its copy of flag, also 2, and writes to flag, flag is 1.