C++ 如何断言std::mutex是否已锁定?
对于GCC4.8.2(在Linux/Debian/Sid 64位上)或GCC4.9(如果可用的话),在C++11中,我有一些互斥锁C++ 如何断言std::mutex是否已锁定?,c++,linux,gcc,c++11,C++,Linux,Gcc,C++11,对于GCC4.8.2(在Linux/Debian/Sid 64位上)或GCC4.9(如果可用的话),在C++11中,我有一些互斥锁 std::mutex gmtx; 实际上,它是某个类Foo中的static成员,包含下面的alpha和beta方法 它被锁定在alpha中,就像 void alpha(void) { std::lock_guard<std::mutex> g(gmtx); beta(void); // some other work } (请注意,
std::mutex gmtx;
实际上,它是某个类Foo
中的static
成员,包含下面的alpha
和beta
方法
它被锁定在alpha
中,就像
void alpha(void) {
std::lock_guard<std::mutex> g(gmtx);
beta(void);
// some other work
}
(请注意,被锁定
仅在断言
中调用……它可能效率很低,甚至有时不准确)
当然,我还有其他函数调用beta
,例如
void gamma(void) {
std::lock_guard<std::mutex> g(gmtx);
beta();
// some other work
}
void伽马(void){
标准:锁紧装置g(gmtx);
β();
//其他工作
}
但是已锁定
不存在。。。。我应该如何定义它?(实际上,我想确定互斥锁已被某个[间接]调用方锁定在同一线程中…)
(我想用assert
测试它的原因是beta
可以在别处调用)
我不能使用try\u lock
(除非使用递归互斥锁),因为在常见情况下,它会锁定一个已经锁定的互斥锁。。。(被调用者锁定在同一个线程中)这不仅是未定义的行为,而且完全阻塞
我希望避免递归互斥(比普通互斥代价更高),除非我真的必须这样做
注意:真正的程序有点复杂。实际上,所有的方法都在一个类中,该类在“items”上保持命名双向关系。所以我在这个类中有一个从项目到名称的映射,还有一个从名称到项目的映射
beta
将是真正添加命名的内部方法,alpha
和gamma
将是按项目名称查找或添加项目或按项目名称查找名称的方法
PS:真正的程序尚未发布,但应该成为其未来的一部分;您可以从(临时位置)下载它(alpha-stage,非常有缺陷)您可以使用一个递归互斥锁,它可以在同一个线程上被多次锁定。注意:如果它是我的代码,我会重新构造它,这样我就不需要一个
递归互斥锁了,但它可以解决您的问题。好吧,如果断言的开销真的不是问题,那么您可以从另一个线程调用try\u 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
}
std::unique_lock
具有拥有_lock
成员函数(正如您所说,相当于已锁定
)
std::互斥gmtx;
std::唯一锁定glock(gmtx,std::延迟锁定);
空洞alpha(空洞){
标准:锁紧护罩g(格洛克);
β(无效);
//其他工作
}
无效测试版(无效){
assert(glock.owns_lock());//或只是assert(glock);
//一些真正的工作
}
编辑:在此解决方案中,所有锁定操作都应通过unique_lockglock
而不是“原始”互斥锁gmtx
执行。例如,alpha
成员函数用lock\u-guard
(或者简单地说是lock\u-guard
)重写。严格来说,问题是直接检查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::atomic<std::thread::id> m_holder;
#endif // #ifndef NDEBUG
};
类互斥体:
公共std::互斥
{
公众:
#ifndef NDEBUG
无效锁()
{
std::mutex::lock();
m_holder=std::this_thread::get_id();
}
#endif/#ifndef NDEBUG
#ifndef NDEBUG
无效解锁()
{
m_holder=std::thread::id();
std::mutex::unlock();
}
#endif/#ifndef NDEBUG
#ifndef NDEBUG
/**
*@return true如果互斥锁被此方法的调用方锁定*/
布尔被调用方()常量锁定
{
返回m_holder==std::this_thread::get_id();
}
#endif/#ifndef NDEBUG
私人:
#ifndef NDEBUG
标准::原子m_支架;
#endif/#ifndef NDEBUG
};
注意以下几点:
在发布模式下,除了可能的构造/销毁(这对于互斥对象来说不是问题)之外,这在std::mutex上没有任何开销
只有在获取互斥锁和释放互斥锁之间才能访问m_holder
成员。因此,互斥锁本身充当m_holder
的互斥锁。由于对类型std::thread::id
的假设非常弱,调用方锁定的将正常工作
其他STL组件,例如,std::lock\u guard
都是模板,因此它们可以很好地与这个新类配合使用
试试(例如,atomic
或atomic
),它有一个很好的功能,可以做你想做的事情,还有其他很好的功能,比如compare\u exchange\u strong。我的解决方案很简单,使用Try\u lock进行测试,然后在需要时解锁:
std::mutex mtx;
bool is_locked() {
if (mtx.try_lock()) {
mtx.unlock();
return false;
}
return true; // locked thus try_lock failed
}
从技术上讲,这不是一个断言,但我使用了一种类似的方法来防止对共享状态的解锁访问:在不安全函数(在您的示例中为beta)中的上为lock guard类添加一个引用参数。然后,除非调用方创建了锁保护,否则无法调用该函数。它解决了在锁之外意外调用函数的问题,并且在编译时这样做,没有争用
因此,以你的例子:
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);
}
typedef std::lock_-guard LockGuard;
空洞alpha(空洞){
锁具g(gmtx);
β(g);
//其他工作
}
无效测试版(锁具和){
//一些真正的工作
}
空隙伽马(空隙){
锁具g(gmtx);
β(g);
//其他工作
}
//也递归地工作
void delta(锁具和g)
{
β(g);
}
缺点:
- 不验证锁是否实际包装了正确的互斥锁
- 需要伪参数。实际上,我通常将这些不安全的函数保密,因此这不是问题
如果beta
要求始终锁定互斥锁(正如您的断言
建议的那样),您为什么要将这一责任推到调用链的上游?因为(正如我编辑的)其他一些函数,如gamma
会调用beta
…对吗
std::mutex mtx;
bool is_locked() {
if (mtx.try_lock()) {
mtx.unlock();
return false;
}
return true; // locked thus try_lock failed
}
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);
}