std::lock_guard的有条件使用

Conditional use of std::lock_guard

本文关键字:有条件 guard lock std      更新时间:2023-10-16

我有一个函数,其中语句foo应该在lock_guard下执行,但只有当指向mutex对象的指针作为参数提供给该函数时。否则,foo不必受到lock_guard保护。

我不能if中使用lock_guard,因为当if块结束时,锁将立即释放。

所以,这个代码是无稽之谈:

bar( std::mutex * optionalMutex = nullptr )
{
   ...
   if ( nullptr != optionalMutex ) {
      std::lock_guard<std::mutex> lockScope( *optionalMutex );
   }          <- Here the lock ends
   foo...     <- foo is not protected when optionalMutex was provided
}

我试过这样的东西:

bar( std::mutex * optionalMutex = nullptr )
{
   ...
   nullptr == optionalMutex ? 0 : std::lock_guard<std::mutex> lockScope( *optionalMutex );
   // this scope should be protected by lock_guard when optionalMutex was provided
   foo...
}

或多或少,对我来说,唯一可能的解决方案是重复foo:

bar( std::mutex * optionalMutex = nullptr )
{
   ...
   if ( nullptr != optionalMutex ) {
      std::lock_guard<std::mutex> lockScope( *optionalMutex );
      foo...
   }  else {
      foo...
   }
}

编译器gcc 4.9.3不编译第二个示例,并抱怨:error: expected primary-expression before 'lockScope'更新:Superlokkus在回答中解释了原因。

但我确实想避免任何代码重复,因此也要避免重复的foo

我的问题:

是否有一种优雅的方法来实现这个问题,以及而不是使用重复的foo。我知道,我可以使用lambda函数来foo,但我很好奇是否有其他解决方案。

这个怎么样?

void bar(std::mutex * optionalMutex = nullptr)
{
        auto lockScope = (optionalMutex == nullptr) ? 
                           std::unique_lock<std::mutex>() 
                         : std::unique_lock<std::mutex>(*optionalMutex);
}

说明:您的编译器在之前的语句中遇到了问题,因为您不能突然更改三元?表达式的类型;即文字CCD_ 16不是CCD_。所以我把这两个分支改为相同的类型,这里是std::unique_lock<std::mutex>,因为lock_guard不是在没有有效互斥的情况下使用的。但在更简单的情况下,仍然更喜欢std::lock_guard而不是std::unique_lock,因为它会使代码更可读。

此外,您的语句对编译器来说是不可行的,即语法正确,因为变量lockScope只存在于一个分支中。

您真正拥有的是两个函数,一个锁定,另一个不锁定。第一个可以调用第二个:

void bar() {
    // whatever
}
void bar(std::mutex* mtx) {
    std::lock_guard<std::mutex> lockScope(*mtx);
    bar();
}

我只有这个解决方案。使用伪mutex对象:

代码为:

bar( std::mutex * optionalMutex = nullptr )
{
   ...
   std::mutex dummyMutex;
   std::lock_guard<std::mutex> lockScope( optionalMutex ? *optionalMutex, dummyMutex );
   foo...     <- NOW foo is protected when optionalMutex was provided
}

这是一个小问题,但您可以通过让调用者传递std::unique_lock来避免传递原始指针:

bar( std::unique_lock<std::mutex> lockScope )
{
    if(lockScope.mutex())
    {
        lockScope.lock();
        //do things
    }
}

这似乎是界面的一个更清晰的表达,并减少了滥用的可能性。

实现自己版本的lock_guard相当简单,它使用指向互斥体的指针而不是引用。

template <typename MutexT>
class conditional_lock_guard
{
private:
    MutexT* const m_mtx;
public:
    explicit conditional_lock_guard(MutexT* mtx) :
        m_mtx{ mtx }
    {
        if (m_mtx != nullptr)
        {
            m_mtx->lock();
        }
    }
    ~conditional_lock_guard() noexcept
    {
        if (m_mtx != nullptr)
        {
            m_mtx->unlock();
        }
    }
    conditional_lock_guard(const conditional_lock_guard&) = delete;
    conditional_lock_guard& operator=(const conditional_lock_guard&) = delete;
    bool owns_lock() const noexcept
    {
        return m_mtx != nullptr;
    }
    explicit operator bool() const noexcept
    {
        return owns_lock();
    }
    MutexT* mutex() const noexcept
    {
        return m_mtx;
    }
};

Superlockus的答案已经足够好了,但我想知道你为什么不简单地这样写:

bar( std::mutex * optionalMutex = nullptr )
{
   if (optionalMutex)
      optionalMutex->lock():
   foo...
   if (optionalMutex)
      optionalMutex->unlock():
}

CCD_ 23和CCD_。