互斥锁没有解锁

mutex lock is not unlocking

本文关键字:解锁      更新时间:2023-10-16

我使用互斥锁来锁定和解锁变量,因为我在更新周期中连续从主线程调用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 stefanatomic_set(&accessing_flag, true);通常会在它们的实现中做与上面描述的相同的事情。如果您的平台上有这些功能,您可能希望使用它们。

首先,您应该使用RAII进行互斥锁/解锁。其次,您要么没有显示直接使用_flag的其他代码,要么您正在使用的互斥锁有问题(不太可能)。什么库提供System::Mutex?

如果System::Mutex正确实现,代码看起来是正确的。有些事要提:

  1. 正如其他人指出的,RAII比宏更好。

  2. 最好将accessingFlag和_flag定义为volatile

  3. 我认为你得到的临时解决方案是不正确的,如果你编译与优化。<>以前bool LoadingScreen: GetFlag (){accessingFlag = true;//可以重新排序或删除Bool值= _flag;//可能会被优化掉accessingFlag = false;//可以在value set之前重新排序返回值;//可以优化为直接返回_flag或register}在上面的代码中,优化器可能会做一些令人讨厌的事情。例如,没有什么可以阻止编译器消除对accessingFlag=true的第一个赋值,或者它可以被重新排序,缓存。例如,从编译器的角度来看,如果是单线程的,对accessingFlag的第一个赋值是无用的,因为值true永远不会被使用。

  4. 使用互斥锁来保护单个bool变量是昂贵的,因为大部分时间花在切换操作系统模式上(从内核到用户来回切换)。使用自旋锁可能不是坏事(具体代码取决于您的目标平台)。它应该是这样的:

    spinlock_lock(&lock);_flag = value;spinlock_unlock(及锁);
  5. 原子变量在这里也很好。它可能看起来像:
atomic_set(&accessing_flag, true);

您考虑过使用CRITICAL_SECTION吗?这只在Windows上可用,所以你失去了一些可移植性,但它是一个有效的用户级互斥锁。

您提供的第二个代码块可能会在读取标志时修改标志,即使在单处理器设置中也是如此。您发布的原始代码是正确的,并且在两个假设下不会导致死锁:

    m_flags锁被正确初始化,并且不被任何其他代码修改。
  1. 锁的实现是正确的。

如果你想要一个可移植的锁实现,我建议使用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;
}