multithreading - 对于通过单个语句访问的变量,我是否需要 QMutex?
问题描述
在本文档中,QMutex 用于保护“数字”不被多个线程同时修改。我有一个代码,其中一个线程被指示根据另一个线程设置的标志做不同的工作。
//In thread1
if(flag)
dowork1;
else
dowork2;
//In thread2
void setflag(bool f)
{
flag=f;
}
我想知道是否需要 QMutex 来保护标志,即
//In thread1
mutex.lock();
if(flag)
{
mutex.unlock();
dowork1;
}
else
{
mutex.unlock();
dowork2;
}
//In thread2
void setflag(bool f)
{
mutex.lock();
flag=f;
mutex.unlock();
}
该代码与文档的不同之处在于两个线程中通过单个语句访问(读取/写入)标志,并且只有一个线程修改标志的值。
PS: 我总是在多线程编程教程中看到一个线程执行“count++”,另一个线程执行“count--”的示例,并且教程说您应该使用互斥锁来保护变量“count”。我无法理解使用互斥锁的意义。是否意味着单条语句“count++”或“count--”的执行可以在中间被打断并产生意想不到的结果?能得到什么意想不到的结果?
解决方案
是否意味着单条语句“count++”或“count--”的执行可以在中间被打断并产生意想不到的结果?能得到什么意想不到的结果?
只是回答这部分:是的,执行可以在语句中间中断。
让我们想象一个简单的案例:
class A {
void foo(){
++a;
}
int a = 0;
};
单个语句++a
在汇编中被翻译为
mov eax, DWORD PTR [rdi]
add eax, 1
mov DWORD PTR [rdi], eax
这可以看作
eax = a;
eax += 1;
a = eax;
如果在 2 个不同线程的同一实例上foo()
调用(无论是在单个内核上还是在多个内核上),您无法预测程序的结果。A
它可以表现得很好:
thread 1 > eax = a // eax in thread 1 is equal to 0
thread 1 > eax += 1 // eax in thread 1 is equal to 1
thread 1 > a = eax // a is set to 1
thread 2 > eax = a // eax in thread 2 is equal to 1
thread 2 > eax += 1 // eax in thread 2 is equal to 2
thread 2 > a = eax // a is set to 2
或不:
thread 1 > eax = a // eax in thread 1 is equal to 0
thread 2 > eax = a // eax in thread 2 is equal to 0
thread 2 > eax += 1 // eax in thread 2 is equal to 1
thread 2 > a = eax // a is set to 1
thread 1 > eax += 1 // eax in thread 1 is equal to 1
thread 1 > a = eax // a is set to 1
在一个定义良好的程序中,N 次调用foo()
应该导致a == N
. 但是从多个线程调用foo()
同一个实例会产生未定义的行为。A
没有办法知道a
N 次调用后的值foo()
。这取决于你如何编译你的程序,使用了哪些优化标志,使用了哪个编译器,你的 CPU 的负载是多少,你的 CPU 的核心数,......
注意
class A {
public:
bool check() const { return a == b; }
int get_a() const { return a; }
int get_b() const { return b; }
void foo(){
++a;
++b;
}
private:
int a = 0;
int b = 0;
};
现在我们有一个类,对于外部观察者来说,它始终保持a
相等b
。优化器可以将此类优化为:
class A {
public:
bool check() const { return true; }
int get_a() const { return a; }
int get_b() const { return b; }
void foo(){
++a;
++b;
}
private:
int a = 0;
int b = 0;
};
因为它不会改变程序的可观察行为。
但是,如果您通过从多个线程调用 A 的同一实例上的 foo() 来调用未定义的行为,您最终可能会出现 if a = 3
,b = 2
并且check()
仍然返回true
。你的代码失去了意义,程序没有做它应该做的事情,并且可以做任何事情。
从这里您可以想象更复杂的情况,例如如果 A 管理网络连接,您最终可以将客户端 #10 的数据发送到客户端 #6。如果您的程序在工厂中运行,您最终可能会激活错误的工具。
如果你想定义未定义的行为,你可以看这里:https : //en.cppreference.com/w/cpp/language/ub 和 C++ 标准为了更好地理解 UB,你可以寻找关于该主题的 CppCon 会谈.
推荐阅读
- javascript - 从 Electron 中的最小化恢复后,我们可以强制 mainWindow 专注于一些 DOM 吗?
- spring-webflow-2 - spring webflow的on-start元素什么时候执行?
- javascript - 如何全选并重置传单中的所有选定标记
- typeorm - 如何使用 TypeORM 查询多对多关系
- ios - 如何更改对象的位置 Swift SceneKit
- visual-studio - 如何计算 Visual Studio 2017 中选定(或找到)的字符和行?
- asp.net - 我想编写显示使用 FileUpload 的 ASP.NET 代码
- javascript - 如何等待来自订阅的异步数据
- php - Laravel 控制器和帮助文件:视图中未定义的变量
- asp.net - Web API 2 登录