我不清楚互斥锁和锁是如何工作的。
我有一个对象 (my_class),我在主线程中从对象中添加、删除和读取数据。在我的第二个线程中,我想检查我的对象中的一些数据。问题是,在从第二个线程读取数据的过程中,当我在主线程中删除对象时,可能会导致应用程序崩溃。
因此我创建了std::lock_guard<std::mutex> lock(mymutex)
在我的第二个线程中。
我创建了测试,有了这个 lock_guard,它永远不会崩溃。但是我不知道我是否也需要在主线程中使用锁。
问题是,当第二个线程锁定 mutex 并读取数据并且主线程想要从对象中删除数据但没有锁定时会发生什么? 否则,当主线程从对象中删除数据时,第二个线程想要锁定互斥量并从对象中读取数据时会发生什么情况?
最佳答案
暂时忘掉std::lock_guard
。这只是方便(一个非常有用的,但仍然只是方便)。同步原语是互斥锁本身。
Mutex 是 MUTual EXclusion 的缩写。它是一种同步原语,允许一个线程排除其他线程访问受互斥锁保护的任何内容。它通常是共享数据,但也可以是任何东西(例如一段代码)。
在你的例子中,你有两个线程共享的数据。为了防止潜在的灾难性并发访问,所有 对该数据的访问都必须受到某种保护。为此使用互斥体是明智的。
因此,您在概念上将数据与互斥量捆绑在一起,并且每当任何代码想要访问(读取、修改、写入、删除...)数据时,它都必须首先锁定互斥量。由于在任何时候都不会再有一个线程锁定互斥量,因此数据访问将正确同步并且不会出现竞争条件。
有了上面的代码,所有访问数据的代码看起来像这样:
mymutex.lock();
/* do whatever necessary with the shared data */
mymutex.unlock();
可以,只要
- 你永远不会忘记正确匹配
lock
和unlock
调用,即使存在多个返回路径,也是如此 - 在互斥量被锁定时完成的操作不会抛出异常
由于上述几点很难手动正确处理(它们是一个很大的维护负担),因此有一种方法可以使它们自动化。这就是我们在开始时搁置的 std::lock_guard
便利。这只是一个简单的 RAII在其构造函数中调用互斥体上的 lock()
并在其析构函数中调用 unlock()
的类。使用锁保护器,访问共享数据的代码将如下所示:
{
std::lock_guard<std::mutex> g(mymutex);
/* do whatever necessary with the shared data */
}
这保证了在操作完成时互斥量将被正确解锁,无论是通过潜在的许多 return
(或其他跳转)语句之一,还是通过异常。
关于c++ 理解多线程中的 lock_guard 和 mutex,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50856715/