Qt:如果只有一个写线程,我需要一个互斥锁吗?



配置:调试结果:程序退出代码为0.

配置:释放结果:主线程无限循环,不会跳出循环(t)。N ==0 = true)。

如何跳出循环?

只有一个写线程,所以我没有使用任何互斥锁。

qt5.13 vs2017

main.cpp:

//#include"QtTestProg.h"
#include<QApplication>
#include<QMessageBox>
#include<QThread>
class MyThread :public QThread
{
Q_OBJECT
public:
int n = 0, m = 1;
void run()override;
};
void MyThread::run()
{
for (; m;) {
QThread::msleep(3000);
n = 1;
}
}
int main(int argc, char*argv[])
{
QApplication a(argc, argv);
MyThread t;
t.start();
for (; 1;)
{
if (t.n != 0)
{
break;
}
}
t.m = 0;
t.quit();
t.wait();
return 0;
}
#include"main.moc"

这个循环:

for (; 1;)
{
if (t.n != 0)
{
break;
}
}

从技术上讲,即使编译器看到t.n没有被修改,上述代码也可以永远运行。理论上,它可以将值缓存到寄存器中或将其优化出来。在进行无锁编程时,还存在缓存一致性问题。

三个选项:

  1. 使用std::atomic在单个变量上具有快速的类似锁的线程安全语义。

  2. t.n的赋值和求值过程中使用std::互斥锁和std::锁。

  3. 使用一个条件变量,使主线程等待n变化,直到运行线程发出信号表明它已经改变。

只有一个写线程,所以我没有使用任何互斥锁。

这是未定义行为。如果从不同的线程读写相同的内存位置,则需要同步。从https://en.cppreference.com/w/cpp/language/memory_model

当一个表达式的求值写入内存位置,而另一个求值读取或修改相同的内存位置时,这两个表达式被称为冲突。有两个相互冲突的求值的程序具有数据争用,除非

  • 两个求值在同一个线程或同一个信号处理程序中执行,或者
  • 两个冲突的求值都是原子操作(参见std::atomic),或者
  • 一个冲突的计算发生在另一个之前(参见std::memory_order)

如果发生了数据争用,程序的行为是未定义的。

你要么需要一个互斥锁来保护读和写,要么使用std::atomic_int/QAtomicInt来代替mn的普通int

至于未定义行为的后果,在这里(godbolt),您可以看到O2级别的gcc在main()中完全编译出循环,除非您将TYPE从顶部的int更改为std::atomic_int

您需要使用QReadWriteLock。这个类是为您的情况而设计的,当您需要几个读取器而不相互阻塞时。

相关内容

最新更新