1

有时编写的函数需要在进入该函数之前锁定一个或多个互斥锁。如果未指定此要求,则可以在进入之前不获取相关锁的情况下调用该函数,这可能会产生灾难性的后果。

现在,可以在函数的文档中指定类似的内容,但我真的不喜欢这样。

我正在考虑在函数的前提条件中指定它(进入函数时断言),但条件应该是什么?

即使 C++11 中的 std::mutex 确实有一个 has_lock() 函数,仍然不能保证是那个拥有锁的人。

4

2 回答 2

3

如果您真的不想使用递归互斥锁,并且您的目标是确定当前线程是否持有互斥锁而不尝试获取它,那么定义互斥锁包装器可能是最简单的解决方案。这是一个狂野的镜头:

#include <thread>
#include <mutex>
#include <iostream>

using namespace std;

template<typename M>
struct mutex_wrapper
{
    void lock() 
    { 
        m.lock(); 
        lock_guard<mutex> l(idGuardMutex); 
        threadId = this_thread::get_id();
    }

    void unlock() 
    { 
        lock_guard<mutex> l(idGuardMutex); 
        threadId = thread::id(); 
        m.unlock(); 
    }

    bool is_held_by_current_thread() const 
    { 
        lock_guard<mutex> l(idGuardMutex); 
        return (threadId == this_thread::get_id()); 
    }

private:

    mutable mutex idGuardMutex;
    thread::id threadId;
    M m;
};

这是一个如何使用它的简单示例:

int main()
{
    cout << boolalpha;
    mutex_wrapper<mutex> m;
    m.lock();
    cout << m.is_held_by_current_thread() << endl;
    m.unlock();
    cout << m.is_held_by_current_thread() << endl;
}
于 2013-01-25T15:10:12.243 回答
2

我认为您的困境的答案就是不要使用外部互斥锁。如果一个类管理需要同步的资源,那么它应该使用内部互斥锁并自己处理所有同步。外部互斥锁很危险,因为它使您面临死锁和非同步访问的可能性。

从评论中,听起来您正在努力解决的问题是重构同步集合。您想将一些代码移出类,但该代码必须同步。这是一个如何做到这一点的示例:

class MyCollection {
private:
    std::list<Foo> items;
    std::mutex lock;

public:
    template <class F> void ForEach( F function )
    {
        std::lock_guard<decltype(this->lock) guard( this->lock );

        for( auto item : items )
            function( *item );
    }
};

这种技术仍然存在死锁的可能性。由于函数参数是一个任意函数,它可能会访问集合并因此获取互斥锁。另一方面,如果“ForEach”应该是只读的,则可能需要这种行为。

于 2013-01-25T15:44:13.673 回答