linux 如何Assertstd::mutex是否被锁定?

iyfjxgzm  于 2023-06-21  发布在  Linux
关注(0)|答案(7)|浏览(156)

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

std::mutex gmtx;

实际上,它是某个类Foo中的static成员,该类包含下面的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不存在我该如何定义它?(实际上,我想确定互斥锁已经被某个[间接]调用者锁定在同一个线程中...)
(the我想用assert测试的原因是beta可以在其他地方调用)
我不能使用try_lock(除非使用递归互斥锁),因为在通常情况下,它会锁定一个已经锁定的互斥锁。(被调用者锁定在同一线程中),这不仅是未定义的行为,而且完全阻塞。
我想避免递归互斥(比普通互斥代价更高),除非我真的必须这样做。
NB:真实的的程序要复杂一些。实际上,所有的方法都在一个类中,这个类在“项”上保持一个双向的命名关系。在这个类中,有一个从项到名称的Map,还有一个从名称到项的Map。beta将是添加实际命名的内部方法,而alphagamma将是通过名称查找或添加项目或通过项目查找名称的方法。
PS:真实的的程序还没有发布,但应该成为MELT的一部分-它的未来monitor;你可以从here(一个临时的位置)下载它(alpha阶段,非常bug)

snvhrwxg

snvhrwxg1#

严格来说,这个问题是关于直接检查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
    bool try_lock()
    {
        if (std::mutex::try_lock()) {
            m_holder = std::thread::id();
            return true;
        }
        return false;
    }
#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::atomic<std::thread::id> m_holder = std::thread::id{};
#endif // #ifndef NDEBUG
};

请注意以下内容:
1.在释放模式下,除了构造/析构(这对于互斥对象来说不是问题)之外,这与std::mutex相比开销为零。

  1. m_holder成员仅在获取互斥体和释放互斥体之间被访问。因此,互斥体本身充当m_holder的互斥体。在std::thread::id类型的假设非常弱的情况下,locked_by_caller将正确工作。
    1.其他标准库类型(如std::lock_guard)是模板,因此它们可以很好地与这个新类一起工作,因为它满足了Mutex的要求。
ccrfmcuu

ccrfmcuu2#

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
}

**EDIT:**在此方案中,所有的锁操作都应该通过unique_lock glock执行,而不是通过'raw' mutex gmtx执行。例如,alpha成员函数被重写为lock_guard<unique_lock<mutex>>(或简称为lock_guard<decltype(glock)>)。

quhf5bfb

quhf5bfb3#

你可以只使用一个recursive_mutex,它可以在同一个线程上被多次锁定。注意:如果是我的代码,我会重新构造它,这样我就不需要recursive_mutex,但它会解决你的问题。

nvbavucw

nvbavucw4#

好吧,如果Assert的开销真的不是问题,那么你可以从另一个线程调用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
}
clj7thdc

clj7thdc5#

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

zynd9foi

zynd9foi6#

从技术上讲,这不是一个Assert,但我已经使用了类似的方法来防止对共享状态的解锁访问:在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);
}

缺点:

  • 不验证锁是否实际 Package 了正确的互斥体。
  • 需要虚拟参数。在实践中,我通常将这种不安全的函数保持为私有,因此这不是问题。
ulydmbyx

ulydmbyx7#

我的解决方案很简单,使用try_lock来测试,然后在需要时解锁:

std::mutex mtx;

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

相关问题