从不同的线程访问外部变量是否必须使用互斥锁?
Is mutex mandatory to access extern variable from a different thread?
我正在 Qt/C++ 中开发应用程序。在某些时候,有两个线程:一个是 UI 线程,另一个是后台线程。我必须根据 bool
类型的外部变量的值从后台线程执行一些操作。我通过单击 UI.
上的按钮来设置此值
header.cpp
extern bool globalVar;
mainWindow.cpp
//main ui thread on button click
setVale(bool val){
globalVar = val;
}
backgroundThread.cpp
while(1){
if(globalVar)
//do some operation
else
//do some other operation
}
这里,写入 globalVar
仅在用户单击按钮时发生,而读取持续发生。
所以我的问题是:
- 像上面这种情况,mutex 是必须的吗?
- 如果读取和写入同时发生,是否会导致应用程序崩溃?
- 如果读取和写入同时发生,
globalVar
是否会具有 true
或 false
以外的其他值?
- 最后,OS 是否提供任何类型的锁定机制来防止 read/write 操作通过不同的线程同时访问内存位置?
因为它只是一个简单的布尔值,所以我认为互斥锁有点矫枉过正,您应该改用原子整数。原子将在单个 CPU 时钟内读写,所以不用担心,而且它将是无锁的,如果可能的话总是更好。
如果它更复杂,那么一定要使用互斥锁。
它不会单独崩溃,但您可能会损坏数据,这可能会导致应用程序崩溃。
系统不会为你管理那些东西,你手动做,只要确保对数据的所有访问都通过互斥锁。
编辑:
由于您多次指定不需要复杂的解决方案,因此您可以选择简单地使用互斥锁而不是 bool。没有必要用互斥量来保护布尔值,因为你可以把互斥量当作布尔值来使用,是的,你可以使用原子,但这就是互斥量已经做的事情(在递归互斥量的情况下加上一些额外的功能).
您的确切工作量也很重要,因为您的示例在实践中没有多大意义。了解那些 some operation
是什么会很有帮助。
因此,在您的 ui 线程中,您可以简单地 val ? mutex.lock() : mutex.unlock()
,而在您的辅助线程中,您可以使用 if (mutex.tryLock()) doStuff; mutex.unlock(); else doOtherStuff;
。现在,如果辅助线程中的操作花费的时间太长,并且您恰好正在更改主线程中的锁,这将阻塞主线程,直到辅助线程解锁。你可以在主线程中使用 tryLock(timeout)
,这取决于你喜欢什么,lock()
将阻塞直到成功,而 tryLock(timeout)
将阻止阻塞但锁定可能会失败。另外,注意不要从您锁定的线程以外的线程解锁,也不要解锁已经解锁的互斥量。
根据您的实际操作,异步事件驱动方法可能更合适。你真的需要 while(1)
吗?您执行这些操作的频率如何?
循环
while(1){
if(globalVar)
//do some operation
else
//do some other operation
}
是busy waiting, which is extremely wasteful. Thus, you're probably better off with some classic synchronization that will wake the background thread (mostly) when there is something to be done. You should consider adapting this example of std::condition_variable
。
假设您开始于:
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
bool ready = false;
你的工作线程可以是这样的:
void worker_thread()
{
while(true)
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
ready = false;
lk.unlock();
}
通知线程应该做这样的事情:
{
std::lock_guard<std::mutex> lk(m);
ready = true;
}
cv.notify_one();
In situation like above does mutex is necessary?
互斥锁是一种有用的工具。你真正需要的是三样东西:
一种确保原子更新的方法(bool 会给你这个,因为它被标准强制为整数类型)
一种确保一个线程的写入效果在另一个线程中实际可见的方法。这听起来可能违反直觉,但c++内存模型是单线程的,优化(软件和硬件)不需要考虑跨线程通信,并且...
一种防止编译器(和CPU!!)重新排序读写的方法。
隐含问题的答案是'yes'。您将需要做所有这些事情(见下文)
If read and write happend at the same time does this cause to crash the application?
不是当它是布尔值时,但程序不会按您预期的方式运行。事实上,由于该程序现在表现出未定义的行为,您根本无法再推断其行为。
If read and write happens at same time, is globalVar going to have some value other thantrue or false?
在这种情况下不是,因为它是一种固有(原子)类型。
And is it going to happen the access(read/write) of a memory location at same time by different thread, does OS providing any kind of locking mechanism to prevent it?
除非您指定一个,否则不会。
您的选择是:
std::atomic<bool>
std::mutex
std::atomic_signal_fence
现实地说,只要你使用整数类型(不是bool
),使它成为volatile
,并通过正确对齐它的存储来保持在它自己的缓存行内,你就不会根本不需要做任何特别的事情。
In situation like above does mutex is necessary?
只有当你想保持变量的值与其他状态同步时。
If read and write happed at the same time does this cause to crash the application?
根据 C++ 标准,这是未定义的行为。所以任何事情都可能发生:例如您的应用程序可能不会崩溃,但它的状态可能会被巧妙地破坏。但是,在现实生活中,编译器通常会提供一些理智的实现定义行为,除非您的平台真的很奇怪,否则您会没事的。任何常见的东西,比如 32 位和 64 位英特尔、PPC 和 ARM 都可以。
If read and write happens at same time, is globalVar going to have some value other thantrue or false?
globalVar
只能有这两个值,所以除非你在谈论它的二进制表示,否则谈论任何其他值是没有意义的。是的,二进制表示可能不正确,而不是编译器所期望的。这就是为什么您不应该使用 bool
而应该使用 uint8_t
的原因。
我不希望在代码审查中看到这样的标志,但是如果 uint8_t
标志是解决您正在解决的任何问题的最简单的解决方案,我会说去做吧。 if (globalVar)
测试会将零视为 false
,将任何其他视为 true
,因此临时 "gibberish" 是可以的,并且在实践中不会产生任何奇怪的影响。当然,根据标准,您将面临未定义的行为。
And is it going to happen the access(read/write) of a memory location at same time by different thread, does OS providing any kind of locking mechanism to prevent it?
这不是 OS 的工作。
不过说到实践:在任何合理的平台上,使用 std::atomic_bool
不会比使用裸 uint8_t
产生任何开销,所以只需使用它就可以了。
我正在 Qt/C++ 中开发应用程序。在某些时候,有两个线程:一个是 UI 线程,另一个是后台线程。我必须根据 bool
类型的外部变量的值从后台线程执行一些操作。我通过单击 UI.
header.cpp
extern bool globalVar;
mainWindow.cpp
//main ui thread on button click
setVale(bool val){
globalVar = val;
}
backgroundThread.cpp
while(1){
if(globalVar)
//do some operation
else
//do some other operation
}
这里,写入 globalVar
仅在用户单击按钮时发生,而读取持续发生。
所以我的问题是:
- 像上面这种情况,mutex 是必须的吗?
- 如果读取和写入同时发生,是否会导致应用程序崩溃?
- 如果读取和写入同时发生,
globalVar
是否会具有true
或false
以外的其他值? - 最后,OS 是否提供任何类型的锁定机制来防止 read/write 操作通过不同的线程同时访问内存位置?
因为它只是一个简单的布尔值,所以我认为互斥锁有点矫枉过正,您应该改用原子整数。原子将在单个 CPU 时钟内读写,所以不用担心,而且它将是无锁的,如果可能的话总是更好。
如果它更复杂,那么一定要使用互斥锁。
它不会单独崩溃,但您可能会损坏数据,这可能会导致应用程序崩溃。
系统不会为你管理那些东西,你手动做,只要确保对数据的所有访问都通过互斥锁。
编辑:
由于您多次指定不需要复杂的解决方案,因此您可以选择简单地使用互斥锁而不是 bool。没有必要用互斥量来保护布尔值,因为你可以把互斥量当作布尔值来使用,是的,你可以使用原子,但这就是互斥量已经做的事情(在递归互斥量的情况下加上一些额外的功能).
您的确切工作量也很重要,因为您的示例在实践中没有多大意义。了解那些 some operation
是什么会很有帮助。
因此,在您的 ui 线程中,您可以简单地 val ? mutex.lock() : mutex.unlock()
,而在您的辅助线程中,您可以使用 if (mutex.tryLock()) doStuff; mutex.unlock(); else doOtherStuff;
。现在,如果辅助线程中的操作花费的时间太长,并且您恰好正在更改主线程中的锁,这将阻塞主线程,直到辅助线程解锁。你可以在主线程中使用 tryLock(timeout)
,这取决于你喜欢什么,lock()
将阻塞直到成功,而 tryLock(timeout)
将阻止阻塞但锁定可能会失败。另外,注意不要从您锁定的线程以外的线程解锁,也不要解锁已经解锁的互斥量。
根据您的实际操作,异步事件驱动方法可能更合适。你真的需要 while(1)
吗?您执行这些操作的频率如何?
循环
while(1){
if(globalVar)
//do some operation
else
//do some other operation
}
是busy waiting, which is extremely wasteful. Thus, you're probably better off with some classic synchronization that will wake the background thread (mostly) when there is something to be done. You should consider adapting this example of std::condition_variable
。
假设您开始于:
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
bool ready = false;
你的工作线程可以是这样的:
void worker_thread()
{
while(true)
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
ready = false;
lk.unlock();
}
通知线程应该做这样的事情:
{
std::lock_guard<std::mutex> lk(m);
ready = true;
}
cv.notify_one();
In situation like above does mutex is necessary?
互斥锁是一种有用的工具。你真正需要的是三样东西:
一种确保原子更新的方法(bool 会给你这个,因为它被标准强制为整数类型)
一种确保一个线程的写入效果在另一个线程中实际可见的方法。这听起来可能违反直觉,但c++内存模型是单线程的,优化(软件和硬件)不需要考虑跨线程通信,并且...
一种防止编译器(和CPU!!)重新排序读写的方法。
隐含问题的答案是'yes'。您将需要做所有这些事情(见下文)
If read and write happend at the same time does this cause to crash the application?
不是当它是布尔值时,但程序不会按您预期的方式运行。事实上,由于该程序现在表现出未定义的行为,您根本无法再推断其行为。
If read and write happens at same time, is globalVar going to have some value other thantrue or false?
在这种情况下不是,因为它是一种固有(原子)类型。
And is it going to happen the access(read/write) of a memory location at same time by different thread, does OS providing any kind of locking mechanism to prevent it?
除非您指定一个,否则不会。
您的选择是:
std::atomic<bool>
std::mutex
std::atomic_signal_fence
现实地说,只要你使用整数类型(不是bool
),使它成为volatile
,并通过正确对齐它的存储来保持在它自己的缓存行内,你就不会根本不需要做任何特别的事情。
In situation like above does mutex is necessary?
只有当你想保持变量的值与其他状态同步时。
If read and write happed at the same time does this cause to crash the application?
根据 C++ 标准,这是未定义的行为。所以任何事情都可能发生:例如您的应用程序可能不会崩溃,但它的状态可能会被巧妙地破坏。但是,在现实生活中,编译器通常会提供一些理智的实现定义行为,除非您的平台真的很奇怪,否则您会没事的。任何常见的东西,比如 32 位和 64 位英特尔、PPC 和 ARM 都可以。
If read and write happens at same time, is globalVar going to have some value other thantrue or false?
globalVar
只能有这两个值,所以除非你在谈论它的二进制表示,否则谈论任何其他值是没有意义的。是的,二进制表示可能不正确,而不是编译器所期望的。这就是为什么您不应该使用 bool
而应该使用 uint8_t
的原因。
我不希望在代码审查中看到这样的标志,但是如果 uint8_t
标志是解决您正在解决的任何问题的最简单的解决方案,我会说去做吧。 if (globalVar)
测试会将零视为 false
,将任何其他视为 true
,因此临时 "gibberish" 是可以的,并且在实践中不会产生任何奇怪的影响。当然,根据标准,您将面临未定义的行为。
And is it going to happen the access(read/write) of a memory location at same time by different thread, does OS providing any kind of locking mechanism to prevent it?
这不是 OS 的工作。
不过说到实践:在任何合理的平台上,使用 std::atomic_bool
不会比使用裸 uint8_t
产生任何开销,所以只需使用它就可以了。