当只有一个线程写入 c++ 中的布尔变量时,是否存在争用条件



在下面的代码示例中,程序执行永远不会结束。

它创建一个线程,该线程在终止之前等待全局bool设置为 true。只有一个作家和一个读者。我相信允许循环继续运行的唯一情况是bool变量是否为假。

bool变量怎么可能最终只使用一个编写器处于不一致的状态?

#include <iostream>
#include <pthread.h>
#include <unistd.h>
bool done = false;
void * threadfunc1(void *) {
    std::cout << "t1:start" << std::endl;
    while(!done);
    std::cout << "t1:done" << std::endl;
    return NULL;
}
int main()
{
    pthread_t threads;
    pthread_create(&threads, NULL, threadfunc1, NULL);
    sleep(1);
    done = true;
    std::cout << "done set to true" << std::endl;
    pthread_exit(NULL);
    return 0;
}

从某种意义上说,这个语句threadfunc1()存在一个问题:

   while(!done);

可以由编译器实现为:

       a_register = done;
   label:
       if (a_register == 0) goto label;

因此,永远不会看到done的更新。

实际上没有什么可以阻止编译器优化while循环。使用原子或互斥锁从多个线程访问布尔值。这是唯一受支持且正确的解决方案。当您使用 posix 时,在这种情况下,互斥锁将是正确的解决方案。

并且不要使用volatile.有一个 posix 标准规定了什么必须有效,volatile不是一个有保证工作的解决方案。

还有一个问题:在将标志设置为 false 之前,无法保证新创建的线程每次都开始运行。

对于这样简单的例子,易失性就足够了。但对于绝大多数现实世界的情况来说,事实并非如此。为此任务使用条件变量。乍一看,它们看起来很奇怪,但实际上它们非常合乎逻辑。在 x86 上,布尔是原子的读/写(对于 ARM,可能不是)。向量也存在一个障碍:它不是布尔值的向量,它是一个位域。要从多个线程写入向量,请使用 vector(或 bool arr[SIZE])。你也没有加入线程,这是错误的。

争用条件意味着:当两个线程访问同一个对象,并且其中至少有一个是写入时。

这意味着您将有两种类型的赛车,写-写冲突和写-读冲突。

回到你的代码,你基本上有两个线程,一个是主线程,另一个是你用pthread_create创建的线程。

其中一个是read: while(!done),另一个是write: done = true。

你肯定有比赛条件。

在 c++ 中只有一个线程写入布尔变量时,是否存在竞争条件?

是的。在您的情况下,主线程也是一个线程(即您有一个线程写入和一个线程读取)。

布尔变量怎么可能最终只用一个编写器处于不一致的状态?

编译器是(应该是)优化编译器。它可能会优化done变量的读取,除非您注意避免这种情况(改用std::atomic<bool> done)。

不能保证分配给一个字节的布尔值是原子的

最新更新