比较是原子操作吗?

发布于 2024-11-03 06:58:26 字数 723 浏览 3 评论 0原文

下面的比较是原子动作吗?即是否可以简化为单个CPU指令?

char flag = 2;

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

    // sleep
}

这就是我正在做的:

int main()
{
    sf::Mutex Mutex;
    char flag = 2;

    coordinatorFunction(flag);

    for(;;)
    {
        if (!flag)
            break;

        // sleep
    }
}

void workerFunction(void* a)
{
    char* p = static_cast<char*>(a);

    // work

    GlobalMutex.Lock();
    --*p;
    GlobalMutex.Unlock();
}

void coordinatorFunction(char& refFlag)
{
    sf::Thread worker1(&workerFunction, &refFlag);
    sf::Thread worker2(&workerFunction, &refFlag);

    worker1.Launch();
    worker2.Launch();
}

Is the following comparison an atomic action? I.e., can it be reduced to a single CPU instruction?

char flag = 2;

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

    // sleep
}

Here's what I'm doing:

int main()
{
    sf::Mutex Mutex;
    char flag = 2;

    coordinatorFunction(flag);

    for(;;)
    {
        if (!flag)
            break;

        // sleep
    }
}

void workerFunction(void* a)
{
    char* p = static_cast<char*>(a);

    // work

    GlobalMutex.Lock();
    --*p;
    GlobalMutex.Unlock();
}

void coordinatorFunction(char& refFlag)
{
    sf::Thread worker1(&workerFunction, &refFlag);
    sf::Thread worker2(&workerFunction, &refFlag);

    worker1.Launch();
    worker2.Launch();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

埖埖迣鎅 2024-11-10 06:58:26

这是错误的做法。

您的主线程正在尽可能快地消耗 CPU 周期,除了等待 flag 达到零之外什么也不做。除最后一次外,每次尝试此测试都会失败。不要以这种方式执行此操作,而是使用线程对象很可能必须使用的“连接”功能来使主线程自行挂起,直到所有工作人员完成为止。

这样,并非巧合,您就不会关心测试是否是原子的,因为您根本不需要它。

This is the wrong way to go about it.

Your main thread is burning up CPU cycles as fast as it can, doing nothing but waiting for flag to reach zero. This test will fail every time it's attempted, except the last one. Instead of doing it in this manner, use the "join" facility that your thread objects most likely have to make the main thread suspend itself until all the workers complete.

This way, not coincidentally, you won't care if the test is atomic because you will not need it at all.

我最亲爱的 2024-11-10 06:58:26

任何 C++ 操作都不保证是原子操作。

None of C++ operations is guaranteed to be atomic operation.

何必那么矫情 2024-11-10 06:58:26

在 C++ 中,没有什么可以保证是原子的。

In C++ nothing is guaranteed to be atomic.

爱她像谁 2024-11-10 06:58:26

不。据我所知,C++ 不保证什么是原子的、什么不是——这是特定于平台的。即使您的平台保证可以原子地进行比较,也不能保证您的 C++ 编译器会选择该指令。

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

如果您想要适当的保证,可以使用各种互锁函数 在 Windows 和 原子gcc 上的内置函数

No. As far as I know, C++ makes no guarantees on what is atomic and what is not - that is platform specific. Even if your platform guarantees that a comparison can be made atomically, there's no guarantee that your C++ compiler will choose that instruction.

However, in practice, a comparison of a simple value type such as a char, int, float, etc is likely to be atomic. You still need to be aware of possible reordering of instructions however, both at the compiler level and the processor level. In this case, that may not matter, but in the general case, it can and does. You also need to be aware that a comparison-then-branch is not atomic even if the comparison is - so 2 threads can both enter the same block of code if you are trying to use a flag to regulate that access.

If you want proper guarantees, there are various Interlocked functions on Windows and the atomic builtins on gcc.

時窥 2024-11-10 06:58:26

不,C++ 不保证任何操作都是原子的。您问题中的代码很可能会被编译为从内存加载到寄存器中,这本身可能需要多个指令,然后进行测试。

No, C++ makes no guarantees that any operation is atomic. The code in your question might well be compiled into a load from memory into a register, which might itself take multiple instructions, followed by a test.

噩梦成真你也成魔 2024-11-10 06:58:26

比较不是原子的,因为它需要几个机器语言指令来完成(从内存加载到寄存器等)。此外,由于内存模型的灵活性和缓存执行测试的线程可能无法“看到”另一个线程的结果马上。

如果变量被标记为易失性,那么您可能可以安全地进行简单测试,但这将是特定于平台的。正如其他人指出的,C++ 本身对此不做任何保证。

The comparison is not atomic because it make take several machine language instructions to do it (load the from memory into a register etc.) Also due to memory model flexibility and caching the thread doing the test may not "see" the results of another thread right away.

You might be safe doing a simple test if the variable is marked volatile but this will be platform-specific. As others noted C++ itself makes no guarantee on this.

追星践月 2024-11-10 06:58:26

比较涉及读取两条数据以及执行实际比较。 数据可以在读取和比较指令之间发生变化,因此它不是原子的。

但是,由于您要比较是否相等,因此 _InterlockedCompareExchange instrinsic(用于 x86 中的 lock cmp xchg 指令)可能会满足您的需要,尽管它会涉及替换数据。

No.

A comparison involves reading both pieces of data as well as performing the actual comparison. The data can change in between the read and the compare instructions, so it's not atomic.

However, because you're comparing for equality, the _InterlockedCompareExchange instrinsic (which is for the lock cmp xchg instructions in x86) might do what you need, although it will involve replacing the data.

脸赞 2024-11-10 06:58:26

你应该问的问题是“是——原子的”?这就是这里最重要的。你想在flag达到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.

因为在1 ns内,主线程循环并再次尝试。

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

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.

您丢失了减量。你想使用 __sync_fetch_and_sub(& flag, 1) 这将自动递减 flag。

最后,在睡眠中旋转并不是最好的方法。您想要等待一个条件,等待 信号。让工作线程在意识到自己已将标志递减为 0 时提出条件或信号。

The question you should be asking is "is -- atomic"? Thats all that matters here. You want to do something when flag reaches 0.

You don't care about this scenario:

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.

Because in 1 ns, the main thread loops around and tries again.

You do care that -- is not atomic and two threads changing it at the same time will skip decrements:

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.

You lost a decrement. you want to use __sync_fetch_and_sub(&flag, 1) which will atomically decrement flag.

Finally, spinning around a sleep is not the best way to do it. You want to either wait on a condition, wait on a signal. Have the worker threads raise the condition or the signal when they realize they have decremented flag to 0.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文