互斥锁没有解锁
mutex lock is not unlocking
我使用互斥锁来锁定和解锁变量,因为我在更新周期中连续从主线程调用getter,并从另一个线程调用setter。我在
下面提供了setter和getter的代码定义bool _flag;
System::Mutex m_flag;
调用#define LOCK(MUTEX_VAR) MUTEX_VAR.Lock();
#define UNLOCK(MUTEX_VAR) MUTEX_VAR.Unlock();
void LoadingScreen::SetFlag(bool value)
{
LOCK(m_flag);
_flag = value;
UNLOCK(m_flag);
}
bool LoadingScreen::GetFlag()
{
LOCK(m_flag);
bool value = _flag;
UNLOCK(m_flag);
return value;
}
这在一半的时间内工作得很好,但有时变量在调用SetFlag时被锁定,因此它永远不会被设置,从而干扰代码流。
谁能告诉我如何解决这个问题?编辑:这是我最终做的解决方案。这只是一个临时的解决方案。如果谁有更好的答案,请告诉我。
bool _flag;
bool accessingFlag = false;
void LoadingScreen::SetFlag(bool value)
{
if(!accessingFlag)
{
_flag = value;
}
}
bool LoadingScreen::GetFlag()
{
accessingFlag = true;
bool value = _flag;
accessingFlag = false;
return value;
}
您所拥有的问题(其中user1192878暗示)是由于延迟编译器加载/存储。您需要使用内存屏障来实现代码。您可以申报volatile bool _flag;
。但是对于单个CPU系统的编译器内存屏障来说,这是不需要的。多cpu解决方案需要硬件障碍(就在下面的维基百科链接中);硬件屏障确保所有cpu都能看到本地处理器的内存/缓存。在这种情况下,不需要使用mutex
和其他联锁。他们到底完成了什么?它们只会创建死锁,并且不需要。
bool _flag;
#define memory_barrier __asm__ __volatile__ ("" ::: "memory") /* GCC */
void LoadingScreen::SetFlag(bool value)
{
_flag = value;
memory_barrier(); /* Ensure write happens immediately, even for in-lines */
}
bool LoadingScreen::GetFlag()
{
bool value = _flag;
memory_barrier(); /* Ensure read happens immediately, even for in-lines */
return value;
}
只有在同时设置多个值时才需要互斥锁。您也可以将bool
类型更改为sig_atomic_t或LLVM原子。然而,这是相当迂腐的,因为bool
将在几乎所有实际的CPU体系结构上工作。Cocoa的并发页面也提供了一些关于其他API的信息。我相信gcc的内联汇编器与Apple的编译器使用的语法相同;但这可能是错误的。
这个API有一些限制。实例GetFlag()
返回,可以调用SetFlag()
。那么GetFlag()
返回值就过时了。如果您有多个写入器,那么您很容易错过一个SetFlag()
。如果高级逻辑容易出现ABA问题,这一点可能很重要。然而,所有这些问题都存在于有无互斥锁的情况下。内存屏障只解决编译器/CPU不会长时间缓存 SetFlag()
的问题,它将重新读取GetFlag()
中的值。声明volatile bool flag
通常会导致相同的行为,但会产生额外的副作用,并且不能解决多cpu问题。
std::atomic<bool>
As per stefan和atomic_set(&accessing_flag, true);
通常会在它们的实现中做与上面描述的相同的事情。如果您的平台上有这些功能,您可能希望使用它们。
首先,您应该使用RAII进行互斥锁/解锁。其次,您要么没有显示直接使用_flag的其他代码,要么您正在使用的互斥锁有问题(不太可能)。什么库提供System::Mutex?
如果System::Mutex正确实现,代码看起来是正确的。有些事要提:
-
正如其他人指出的,RAII比宏更好。
-
最好将accessingFlag和_flag定义为volatile
-
我认为你得到的临时解决方案是不正确的,如果你编译与优化。<>以前bool LoadingScreen: GetFlag (){accessingFlag = true;//可以重新排序或删除Bool值= _flag;//可能会被优化掉accessingFlag = false;//可以在value set之前重新排序返回值;//可以优化为直接返回_flag或register}在上面的代码中,优化器可能会做一些令人讨厌的事情。例如,没有什么可以阻止编译器消除对accessingFlag=true的第一个赋值,或者它可以被重新排序,缓存。例如,从编译器的角度来看,如果是单线程的,对accessingFlag的第一个赋值是无用的,因为值true永远不会被使用。
-
使用互斥锁来保护单个bool变量是昂贵的,因为大部分时间花在切换操作系统模式上(从内核到用户来回切换)。使用自旋锁可能不是坏事(具体代码取决于您的目标平台)。它应该是这样的:
spinlock_lock(&lock);_flag = value;spinlock_unlock(及锁);
- 原子变量在这里也很好。它可能看起来像:
atomic_set(&accessing_flag, true);
您考虑过使用CRITICAL_SECTION吗?这只在Windows上可用,所以你失去了一些可移植性,但它是一个有效的用户级互斥锁。
您提供的第二个代码块可能会在读取标志时修改标志,即使在单处理器设置中也是如此。您发布的原始代码是正确的,并且在两个假设下不会导致死锁:
- m_flags锁被正确初始化,并且不被任何其他代码修改。
- 锁的实现是正确的。
如果你想要一个可移植的锁实现,我建议使用OpenMP:如何在openMP中使用锁?
从你的描述,似乎你想忙着等待一个线程来处理一些输入。在这种情况下,stefans解决方案(声明标志std::atomic)可能是最好的。在半正常的x86系统上,还可以声明volatile int标志。只是不要对未对齐的字段(打包结构)这样做。
可以避免使用两个锁的繁忙等待。第一个锁在从线程完成处理时被解锁,在等待从线程完成处理时被主线程锁定。第二个锁在提供输入时由主线程解锁,在等待输入时由从线程锁定。
这是我在某处看到的一种技术,但再也找不到源代码了。如果我找到了,我会编辑答案。基本上,写入器只会写入,但读取器将不止一次地读取set变量的值,并且只有当所有副本都一致时,它才会使用它。我已经改变了写入器,这样它就会尝试继续写入值,只要它不匹配它期望的值。
bool _flag;
void LoadingScreen::SetFlag(bool value)
{
do
{
_flag = value;
} while (_flag != value);
}
bool LoadingScreen::GetFlag()
{
bool value;
do
{
value = _flag;
} while (value != _flag);
return value;
}
- 我应该在锁定TBitmap画布后解锁它吗
- 虚假唤醒是否会解锁所有等待线程,甚至是不相关的线程?
- c++ 为什么我不应该从不同的线程解锁互斥锁
- 在新作用域中使用unique_lock是否等效于在使用共享资源的工作结束时解锁调用
- "data race"(不是真的)在通知条件变量并解锁关联的互斥锁后
- 程序输入密码并解锁窗口7,8,10
- 在通知之前完成手动解锁
- STD :: Mutex如何在不同的线程中解锁
- 如何使用单个解锁方法(可称为读取器或写入器)实现C++读写器锁?
- 如何在C 中自动汇总日志消息并自动解锁互斥X
- 如果我们已经手动解锁了unique_lock,那么破坏时会解锁吗?
- 正在解锁手动未定义/不良设计的锁定guard
- 从C 运行代码后解锁绑定(在R中)的问题
- 在功能返回之前,可以解锁Mutex会增加并发
- 当互斥锁解锁时,它会notify_all或notify_one
- 如何确保在C ++中解锁储物柜?哪种解决方案更好
- 我应该如何在一个功能中锁定wxMutex,并在另一个功能中将其解锁
- mutex::lock() 检查一次解锁状态是否已经被另一个线程锁定?
- 在Qt 5.4中可以对互斥对象进行两次解锁吗
- C++线程:等待condition_variable后无法解锁阵列中的互斥锁