如何断言std :: mutex是否被锁定?

时间:2014-02-19 21:33:23

标签: c++ linux gcc c++11

使用GCC 4.8.2(在Linux / Debian / Sid 64位上) - 或GCC 4.9(如果可用) - 在C ++ 11中 - 我有一些互斥量

std::mutex gmtx;

实际上,它是某个类static中的Foo成员,其中包含以下alphabeta方法。

它被锁定在alpha

void alpha(void) {
   std::lock_guard<std::mutex> g(gmtx);
   beta(void);
   // some other work
}

我要检查确实beta被锁定的gmtx

void beta(void) {
   assert (gmtx.is_locked());
   // some real work
}

(请注意,is_locked仅在assert内调用......效率非常低,甚至有时不准确

当然,我还有其他函数调用beta,例如

void gamma(void) {
   std::lock_guard<std::mutex> g(gmtx);
   beta();
   // some other work
}

is_locked不存在......我该如何定义它? (实际上我想确保某个[间接]调用者将互斥锁锁定在同一个线程中...)

(我想用assert对其进行测试的原因是beta可以在别处调用

我不能使用try_lock(除非使用递归互斥锁),因为在常见情况下它会锁定已经锁定的互斥锁...(由调用者锁定在同一个线程中)这不仅是未定义的行为但完全阻止。

我想避免使用递归互斥锁(比普通互斥锁更昂贵),除非我真的需要。


注意:真正的程序有点复杂。实际上,所有方法都在一个类中,它维持“项目”的命名双向关系。所以我在该类中有一个从项目到名称的映射,另一个从名称到项目。 beta将是添加真正命名的内部方法,alphagamma将是查找的方法 - 或者按名称添加项目,或按项目添加名称。“ / SUP>

PS:真正的程序尚未发布,但应该成为MELT的一部分 - 它的未来monitor;你可以从here(临时位置)

下载它(alpha阶段,非常错误)

7 个答案:

答案 0 :(得分:15)

严格地说,问题是关于直接检查std::mutex的锁定性。但是,如果允许将其封装在新类中,则很容易实现:

class mutex :
    public std::mutex
{
public:
#ifndef NDEBUG
    void lock()
    {
        std::mutex::lock();
        m_holder = std::this_thread::get_id(); 
    }
#endif // #ifndef NDEBUG

#ifndef NDEBUG
    void unlock()
    {
        m_holder = std::thread::id();
        std::mutex::unlock();
    }
#endif // #ifndef NDEBUG

#ifndef NDEBUG
    /**
    * @return true iff the mutex is locked by the caller of this method. */
    bool locked_by_caller() const
    {
        return m_holder == std::this_thread::get_id();
    }
#endif // #ifndef NDEBUG

private:
#ifndef NDEBUG
    std::thread::id m_holder;
#endif // #ifndef NDEBUG
};

请注意以下事项:

  1. 在发布模式下,除std::mutex之外,其开销为零,但可能是构造/销毁(这对于互斥对象来说不是问题)。
  2. 仅在获取互斥锁并释放互斥锁之间访问m_holder成员。因此,互斥体本身充当m_holder的互斥体。如果对std::thread::id类型的假设非常弱,locked_by_caller将正常工作。
  3. 其他STL组件,例如std::lock_guard是模板,因此它们适用于这个新类。

答案 1 :(得分:13)

std::unique_lock<L>具有owns_lock成员函数(相当于is_locked)。

std::mutex gmtx;
std::unique_lock<std::mutex> glock(gmtx, std::defer_lock);

void alpha(void) {
   std::lock_guard<decltype(glock)> g(glock);
   beta(void);
   // some other work
}
void beta(void) {
   assert(glock.owns_lock()); // or just assert(glock);
   // some real work
}

编辑:在此解决方案中,所有锁操作都应通过unique_lock glock而非'raw'互斥gmtx执行。例如,使用alpha(或简称lock_guard<unique_lock<mutex>>)重写lock_guard<decltype(glock)>成员函数。

答案 2 :(得分:9)

您可以使用recursive_mutex,它可以在同一个线程上多次锁定。注意:如果是我的代码,我会对其进行重组,以便我不需要recursive_mutex,但它会解决您的问题。

答案 3 :(得分:2)

尝试atomic(例如atomic<bool>atomic<int>),它有一个很好的load函数可以做你想要的,以及其他很好的函数,比如compare_exchange_strong。

答案 4 :(得分:1)

好吧,如果断言的费用确实不是问题,那么你可以从另一个线程调用try_lock(),保证其行为得到很好的定义:

void beta(void) {
  assert(std::async(std::launch::async, [] { return gmtx.try_lock(); })
                 .get() == false &&
         "error, beta called without locking gmtx");
  // some real work
}

答案 5 :(得分:0)

我的解决方案很简单,请使用try_lock进行测试,然后根据需要进行解锁:

std::mutex mtx;

bool is_locked() {
   if (mtx.try_lock()) {
      mtx.unlock();
      return false;
   }
   return true; // locked thus try_lock failed
}

答案 6 :(得分:0)

从技术上讲,这不是一个断言,但是我使用了类似的方法来防止对共享状态的未锁定访问:在unsafe函数(示例中为beta)中的锁保护类中添加引用参数。然后,除非调用者创建了锁卫,否则无法调用该函数。它解决了意外地在锁外部调用该函数的问题,并且它在编译时就没有了争用。

因此,以您的示例为例:

typedef std::lock_guard<std::mutex> LockGuard;
void alpha(void) {
   LockGuard g(gmtx);
   beta(g);
   // some other work
}

void beta(LockGuard&) {
   // some real work
}

void gamma(void) {
   LockGuard g(gmtx);
   beta(g);
   // some other work
}

//works recursively too
void delta(LockGuard& g)
{
   beta(g);
}

缺点:

  • 不验证锁是否包装了正确的互斥锁。
  • 需要虚拟参数。实际上,我通常将这些不安全的函数保密,所以这不是问题。