我创建了以下类,它提供了 acquire_lock()
和 release_lock()
函数
class LockableObject {
public:
void acquire_lock() {
std::unique_lock<std::mutex> local_lock(m_mutex);
m_lock = std::move(local_lock);
}
void release_lock() {
m_lock.unlock();
}
private:
std::mutex m_mutex;
std::unique_lock<std::mutex> m_lock;
};
此类提供了acquire_lock
和release_lock
函数。我有多个线程访问同一个对象,并在执行任何操作之前调用 acquire_lock
,然后在完成后调用 release_lock
,如下所示。
void ThreadFunc(int ID, LockableObject* lckbleObj)
{
for (int i = 0; i < 1000; i++)
{
lckbleObj->acquire_lock();
std::cout << "Thread with ID = " << ID << "doing work" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
lckbleObj->release_lock();
}
}
void main()
{
const int numThreads = 10;
std::thread workerThreads[numThreads];
LockableObject *testObject = new LockableObject();
for (int i = 0; i < numThreads; i++)
{
workerThreads[i] = std::thread(ThreadFunc, i, testObject);
}
for (int i = 0; i < numThreads; i++)
{
workerThreads[i].join();
}
}
在 acquire_lock
函数中,我首先尝试使用本地堆栈 std::unique_lock
对象锁定底层互斥体 (m_mutex
)在构造函数中传递它(m_mutex
)。我假设一旦 std::unique_lock
的构造函数返回,它就锁定了互斥体,然后我将堆栈上的 unique_lock
移动到成员变量 m_lock
。
这个程序在一些基本方面存在缺陷,在调用release_lock
期间将导致“解锁无主互斥体”
,我似乎错过了一些基本的东西std::unique_lock
我正在寻找有人来纠正我的理解。
最佳答案
请参阅我关于缺少 std::defer_lock
的评论在构造函数中。但您的代码中也存在竞争条件。
acquire_lock
函数修改 m_lock
在 m_mutex
的保护下互斥体。这样,为了保证线程安全,其他线程不能修改m_lock
除非按住m_mutex
.
但是release_lock
函数修改m_lock
当它释放该互斥体时。因此,您在 m_lock
上没有正确的同步。 .
这有点难以理解。这是问题代码:
m_lock.unlock();
请注意,当输入此函数时,m_mutex
被锁定,但在执行期间,它都修改 m_lock
并发布m_mutex
没有特别保证的顺序。但是m_mutex
保护m_lock
。所以这是一个竞争条件并且是不允许的。
可以按如下方式修复:
void release_lock() {
std::unique_lock<std::mutex> local_lock = std::move(m_lock);
local_lock.unlock();
}
现在,第一行代码修改 m_lock
但完全以 m_mutex
运行握住。这避免了竞争条件。
unlock
如果需要,可以将其移除。 local_lock
的析构函数会做的。
顺便说一句,我建议更改 API。与其提供锁定和解锁调用,不如有一种方法来创建一个拥有该对象锁的对象。您甚至可以使用std::unique_lock<LockableObject>
如果你想。为什么要创建一个比标准提供的 API 更糟糕的新 API?
关于c++ - 解锁无主互斥体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59638470/