有条件地使用 std::lock_guard
Conditional use of std::lock_guard
我有一个函数,其中语句 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
。我知道,我可以对 group foo
使用 lambda 函数,但我很好奇是否还有其他解决方案。
我只有这个解决方案。使用虚拟 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
}
这个怎么样?
void bar(std::mutex * optionalMutex = nullptr)
{
auto lockScope = (optionalMutex == nullptr) ?
std::unique_lock<std::mutex>()
: std::unique_lock<std::mutex>(*optionalMutex);
}
解释:您的编译器在处理您先前的语句时遇到了问题,因为您不能突然更改三元 ?
表达式的类型;即文字 0
不是 std::lock_guard
,反之亦然。所以我将两个分支更改为相同的类型,这里 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();
}
的答案很好,但我想知道你为什么不直接写成这样:
bar( std::mutex * optionalMutex = nullptr )
{
if (optionalMutex)
optionalMutex->lock():
foo...
if (optionalMutex)
optionalMutex->unlock():
}
lock_guard
和unique_lock
很方便,但不是唯一的方法。
这是一个小问题,但您可以通过让调用者传递 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;
}
};
我有一个函数,其中语句 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
。我知道,我可以对 group foo
使用 lambda 函数,但我很好奇是否还有其他解决方案。
我只有这个解决方案。使用虚拟 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
}
这个怎么样?
void bar(std::mutex * optionalMutex = nullptr)
{
auto lockScope = (optionalMutex == nullptr) ?
std::unique_lock<std::mutex>()
: std::unique_lock<std::mutex>(*optionalMutex);
}
解释:您的编译器在处理您先前的语句时遇到了问题,因为您不能突然更改三元 ?
表达式的类型;即文字 0
不是 std::lock_guard
,反之亦然。所以我将两个分支更改为相同的类型,这里 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();
}
bar( std::mutex * optionalMutex = nullptr )
{
if (optionalMutex)
optionalMutex->lock():
foo...
if (optionalMutex)
optionalMutex->unlock():
}
lock_guard
和unique_lock
很方便,但不是唯一的方法。
这是一个小问题,但您可以通过让调用者传递 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;
}
};