Qt:如果只有一个编写器线程,我是否需要互斥体?
Qt: do I need a mutex if there is only one writer thread?
配置:调试结果:程序退出,代码为 0。
配置:释放结果:主线程无限循环,不会跳出循环(t.n==0为真)
我怎样才能摆脱困境?
只有一个编写器线程,所以我没有使用任何互斥锁。
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
没有被修改,上面的代码也可以永远 运行。从理论上讲,它可以将值缓存在寄存器中或对其进行优化。还有与进行无锁编程相关的缓存一致性问题。
三个选项:
使用std::atomic在单个变量上具有快速的类锁和线程安全语义。
只需在 t.n
的赋值和计算周围使用一个 std::mutex and std::lock。
使用 condition variable 这样主线程会等待 n
发生变化,直到 运行ning 线程发信号通知它已经发生变化。
Only one writer thread,so I didnot use any mutex.
这是未定义的行为。如果您从不同的线程读取和写入相同的内存位置,则需要同步。来自 https://en.cppreference.com/w/cpp/language/memory_model
When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. A program that has two conflicting evaluations has a data race unless
- both evaluations execute on the same thread or in the same signal handler, or
- both conflicting evaluations are atomic operations (see std::atomic), or
- one of the conflicting evaluations happens-before another (see std::memory_order)
If a data race occurs, the behavior of the program is undefined.
您要么需要一个保护读写的互斥锁,要么使用 std::atomic_int
/QAtomicInt
而不是 m
和 n
的普通 int
。
至于未定义行为的后果,here (godbolt) 您可以看到 O2 级别的 gcc 完全编译出 main()
中的循环,除非您将 TYPE
从 int
到顶部的 std::atomic_int
。
您需要使用 QReadWriteLock
。此 class 专为您的情况而设计,当您需要多个读者互不干扰时。
配置:调试结果:程序退出,代码为 0。
配置:释放结果:主线程无限循环,不会跳出循环(t.n==0为真)
我怎样才能摆脱困境?
只有一个编写器线程,所以我没有使用任何互斥锁。
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
没有被修改,上面的代码也可以永远 运行。从理论上讲,它可以将值缓存在寄存器中或对其进行优化。还有与进行无锁编程相关的缓存一致性问题。
三个选项:
使用std::atomic在单个变量上具有快速的类锁和线程安全语义。
只需在
t.n
的赋值和计算周围使用一个 std::mutex and std::lock。使用 condition variable 这样主线程会等待
n
发生变化,直到 运行ning 线程发信号通知它已经发生变化。
Only one writer thread,so I didnot use any mutex.
这是未定义的行为。如果您从不同的线程读取和写入相同的内存位置,则需要同步。来自 https://en.cppreference.com/w/cpp/language/memory_model
When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. A program that has two conflicting evaluations has a data race unless
- both evaluations execute on the same thread or in the same signal handler, or
- both conflicting evaluations are atomic operations (see std::atomic), or
- one of the conflicting evaluations happens-before another (see std::memory_order)
If a data race occurs, the behavior of the program is undefined.
您要么需要一个保护读写的互斥锁,要么使用 std::atomic_int
/QAtomicInt
而不是 m
和 n
的普通 int
。
至于未定义行为的后果,here (godbolt) 您可以看到 O2 级别的 gcc 完全编译出 main()
中的循环,除非您将 TYPE
从 int
到顶部的 std::atomic_int
。
您需要使用 QReadWriteLock
。此 class 专为您的情况而设计,当您需要多个读者互不干扰时。