当然,很多人听到了,但是有人在实践中遇到了诸如僵局和种族状况之类的词。 这些概念被归类为并发使用中的错误。 如果我问您什么是死锁问题,那么毫无疑问,您很有可能会开始绘制经典的死锁图片或其用伪代码表示。 像这样:

我们在研究所获得了这些信息;可以在Internet上的书籍和文章中找到这些信息。 这样的死锁,例如,使用两个互斥锁,可以在代码中找到。 但是在大多数情况下,并不是所有事情都那么简单,并且如果不是以通常的形式呈现的话,并不是每个人都能看到代码中的经典错误模式。

考虑一个我们对StartUpdate,CheckAndUpdate和Stop方法感兴趣的类,使用C ++,代码尽可能简单:
std::recursive_mutex m_mutex; Future m_future; void Stop() { std::unique_lock scoped_lock(m_mutex); m_future.Wait();
您在显示的代码中应注意的是:
- 使用递归互斥。 仅当这些捕获发生在同一线程中时,重复捕获递归互斥锁才不会产生期望。 在这种情况下,互斥量豁免的数量应与捕获数量相对应。 如果我们尝试捕获已在另一个线程中捕获的递归互斥体,则该线程将进入待机模式。
- Future :: Schedule函数在回调传递给它的单独线程中启动(以毫秒为单位)
现在,我们分析所有收到的信息并组成图片:

考虑到以上两个事实,不难得出这样的结论:如果已经在另一个函数中捕获了互斥锁,则尝试捕获其中一个函数的递归互斥锁会导致释放该互斥锁,因为CheckAndUpdate回调始终在单独的线程中执行。
乍一看,死锁没有任何可疑之处。 但更紧密地说,这全都归结为我们的经典画面。 当功能对象开始执行时,我们隐式捕获m_future资源,直接回调
与m_future相关联:
导致死锁的操作顺序如下:- 计划运行CheckAndUpdate,但是在n毫秒后回调不会立即开始。
- 调用Stop方法,然后启动它:我们尝试捕获互斥对象-资源已被捕获,我们开始等待m_future完成-对象尚未被调用,我们正在等待。
- CheckAndUpdate的执行开始:我们试图捕获互斥锁-我们不能,资源已经被另一个线程捕获,我们正在等待释放。
仅此而已:发出Stop调用的线程等待CheckAndUpdate完成,而另一个线程只有抓住前面提到的线程已捕获的互斥量,才能继续工作。 这是一个经典的僵局。 一半的工作已经完成-问题的原因已被发现。
现在介绍一些解决方法。方法1捕获资源的过程应该相同,这样可以避免死锁。 也就是说,您需要查看是否可以在Stop方法中更改资源捕获的顺序。 由于这里的死锁情况并不十分明显,并且CheckAndUpdate中没有明确捕获m_future资源,因此我们决定考虑另一种解决方案,以避免将来再次出现错误。
方法2- 检查是否可以在CheckAndUpdate中选择不使用互斥锁。
- 由于我们使用同步机制,因此我们限制对某些资源的访问。 也许足以将这些资源重新转换为原子(就像我们以前那样),对其进行访问已经是线程安全的。
- 事实证明,访问受限的变量可以轻松转换为原子,因此成功删除了上述互斥锁。
这是一个简单明了的示例,具有明显的死锁,可以轻松地减少此错误的模式。 最后,我希望您编写可靠且线程安全的代码!