C++ lock_guard 与 mutex.lock()

Posted

技术标签:

【中文标题】C++ lock_guard 与 mutex.lock()【英文标题】:C++ lock_guard vs mutex.lock() 【发布时间】:2020-10-27 09:38:28 【问题描述】:

我有一个这样的循环:

for (auto &i : elements)

  std::lock_guard<std::mutex> lock(i.mutex);
  some heavy writing to disk with i

抛出错误:

tpp.c:62: __pthread_tpp_change_priority: Assertion `new_prio == -1 || (new_prio >= __sched_fifo_min_prio && new_prio <= __sched_fifo_max_prio)' failed.

谁能解释一下,为什么这个循环不会抛出错误:

for (auto &i : elements)

  i.mutex.lock();
  some heavy writing to disk with i
  i.mutex.unlock();

我对 C++ 多线程的了解更多是从实用的角度来看,所以两个循环似乎总是和我一样。但是,第一个给我带来的问题比第二个要多得多。它也不总是同样的错误,我也得到了一些无效的循环 #1 指针,而第二个循环在许多运行中还没有崩溃。

任何猜测,在不知道其余代码的情况下可能导致问题的原因是什么?

【问题讨论】:

问题很可能出在您的其余代码中。某处有未定义的行为,无法猜测是什么或在哪里。 但是这不是很奇怪吗,它使用循环#1 在几秒钟内崩溃,但使用循环#2 根本没有崩溃?它说明了其余代码中的错误是什么?一些内存管理不善,因此创建 lock_guard 会导致问题,就像创建任何对象一样? 未定义的行为有时很奇怪,有时它似乎有效。它还可能影响程序中明显不相关的部分。找到它的最有效方法是假设任何事情都可能出错,仔细阅读并更仔细地思考。 尝试改用std::unique_lock 【参考方案1】:

tpp.c:63: __pthread_tpp_change_priority: Assertion 是一个已知问题,solved:

简单来说,这个问题是由于快速互斥锁重复加锁导致的,使用递归互斥锁解决,默认pthread_mutex_t不递归。线程运行代码内部是否有可能存在 pthread_mutex_t ? 顺便说一句,要使互斥锁递归,请使用属性 PTHREAD_MUTEX_RECURSIVE_NP 设置互斥锁属性。

【讨论】:

我也读了这个答案。但我不确定,如果递归互斥锁是我想要的。当我理解正确时,对 unlock() 的调用次数必须等于对 lock() 的调用次数。我认为这不会解决我的问题,而是创建其他问题,因为我不明白是什么部分导致它。 问题是快速互斥体的重复锁定,可能在正确的条件下,经典方法锁定/解锁也会失败。所以锁定/解锁不是正确的解决方案,而只是幸运的解决方案。在第一种情况下,可能调用 lock_guard 的构造函数/析构函数会导致正确的延迟,让问题突出显示。【参考方案2】:

好的,我想我找到了答案。谢谢大家整理东西。 @molbdnilo 最接近问题所在。实际上,在我的其余代码中存在未定义的行为。运行 Valgrind 向我显示了 lock_guard 上的无效读写操作。我的互斥体存储在一个共享指针中,该指针被另一个线程重置,而没有事先被锁定。因此,互斥锁被销毁,而另一个线程在其上持有 lock_guard。我想这是个问题?

在循环 #2 中,实际情况是 i->mutex.lock() 和 i->mutex.unlock()。所以这里没有抛出错误,可能是因为第一个锁有时只是发生在一个对象上,很快就会被释放。然后在已解锁的互斥体上调用解锁,这也是未定义的行为,但目前不会导致任何错误。

【讨论】:

以上是关于C++ lock_guard 与 mutex.lock()的主要内容,如果未能解决你的问题,请参考以下文章

linux C++互斥锁std::lock_guard(轻锁)std::unique_lock(重锁)区别

linux C++互斥锁std::lock_guard(轻锁)std::unique_lock(重锁)区别

c++ unique_lock lock_guard

C++ std::lock_guard 自动加锁释放锁 原理

c++ 如何将 std::mutex 和 std::lock_guard 与仿函数一起使用?

C++ 有 mutex.lock 为什么要用 lock_guard unique_lock