std::timed_mutex::try_lock* 虚假失败
std::timed_mutex::try_lock* fail spuriously
try_lock*
的意思是 try_lock()
、try_lock_for()
和 try_lock_until()
。根据 cppreference,这三种方法都可能虚假地失败。以下引用自 try_lock_for()
的说明
As with try_lock()
, this function is allowed to fail spuriously and
return false
even if the mutex was not locked by any other thread at
some point during timeout_duration
.
我知道 std::condition_variable
可能会发生虚假唤醒及其背后的原理。但是,互斥量是怎么回事?
根据:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3209.htm
On the other hand, there are strong reasons to require that programs be written to tolerate spurious try_lock() failures:
- As pointed out in Boehm, Adve, "Foundations of the C++ Concurrency Memory Model", PLDI 08, enforcing sequential consistency for data-race-free programs without spurious try_lock() failures requires significantly stronger memory ordering for lock() operations on try_lock()-compatible mutex types. On some architectures that significantly increases the cost of uncontended mutex acquisitions. This cost appears to greatly outweigh any benefit from prohibiting spurious try_lock() failures.
- It allows a user-written try_lock() to fail if, for example, the implementation fails to acquire a low-level lock used to protect the mutex data structure. Or it allows such an operation to be written directly in terms of compare_exchange_weak.
- It ensures that client code remains correct when, for example, a debugging thread is introduced that occasionally acquires locks in order to be able to read consistent values from a data structure being checked or examined. Any code that obtains information from try_lock() failure would break with the introduction of another thread that purely locks and reads the data structure.
来自 C++14 章节“30.4.1.2 Mutex 类型”
第 16 段:
An implementation may fail to obtain the lock even if it is not held by any other thread. [Note: This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange (Clause 29). —end note] An implementation should ensure that try_lock()
does not consistently return false
in the absence of contending mutex acquisitions.
第 19 段:
little would be known about the state after a failure, even in the absence of spurious failures
并回答
I know that spurious wakeup may happen with std::condition_variable
and the rationale behind it. But, what is the case with a mutex?
std::timed_mutex
有时在 OS 中没有直接支持时使用 std::condition_varible
实现。在 GNU libstdc++ 中:
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
...
#else // !_GTHREAD_USE_MUTEX_TIMEDLOCK
class timed_mutex
{
mutex _M_mut;
condition_variable _M_cv;
bool _M_locked = false;
public:
template<typename _Rep, typename _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{
unique_lock<mutex> __lk(_M_mut);
if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
return false;
_M_locked = true;
return true;
}
template<typename _Clock, typename _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{
unique_lock<mutex> __lk(_M_mut);
if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
return false;
_M_locked = true;
return true;
}
};
#endif
try_lock*
的意思是 try_lock()
、try_lock_for()
和 try_lock_until()
。根据 cppreference,这三种方法都可能虚假地失败。以下引用自 try_lock_for()
As with
try_lock()
, this function is allowed to fail spuriously and returnfalse
even if the mutex was not locked by any other thread at some point duringtimeout_duration
.
我知道 std::condition_variable
可能会发生虚假唤醒及其背后的原理。但是,互斥量是怎么回事?
根据:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3209.htm
On the other hand, there are strong reasons to require that programs be written to tolerate spurious try_lock() failures:
- As pointed out in Boehm, Adve, "Foundations of the C++ Concurrency Memory Model", PLDI 08, enforcing sequential consistency for data-race-free programs without spurious try_lock() failures requires significantly stronger memory ordering for lock() operations on try_lock()-compatible mutex types. On some architectures that significantly increases the cost of uncontended mutex acquisitions. This cost appears to greatly outweigh any benefit from prohibiting spurious try_lock() failures.
- It allows a user-written try_lock() to fail if, for example, the implementation fails to acquire a low-level lock used to protect the mutex data structure. Or it allows such an operation to be written directly in terms of compare_exchange_weak.
- It ensures that client code remains correct when, for example, a debugging thread is introduced that occasionally acquires locks in order to be able to read consistent values from a data structure being checked or examined. Any code that obtains information from try_lock() failure would break with the introduction of another thread that purely locks and reads the data structure.
来自 C++14 章节“30.4.1.2 Mutex 类型”
第 16 段:
An implementation may fail to obtain the lock even if it is not held by any other thread. [Note: This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange (Clause 29). —end note] An implementation should ensure that
try_lock()
does not consistently returnfalse
in the absence of contending mutex acquisitions.
第 19 段:
little would be known about the state after a failure, even in the absence of spurious failures
并回答
I know that spurious wakeup may happen with std::condition_variable and the rationale behind it. But, what is the case with a mutex?
std::timed_mutex
有时在 OS 中没有直接支持时使用 std::condition_varible
实现。在 GNU libstdc++ 中:
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
...
#else // !_GTHREAD_USE_MUTEX_TIMEDLOCK
class timed_mutex
{
mutex _M_mut;
condition_variable _M_cv;
bool _M_locked = false;
public:
template<typename _Rep, typename _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{
unique_lock<mutex> __lk(_M_mut);
if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
return false;
_M_locked = true;
return true;
}
template<typename _Clock, typename _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{
unique_lock<mutex> __lk(_M_mut);
if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
return false;
_M_locked = true;
return true;
}
};
#endif