是否存在以下情况:解锁然后锁定已解锁的互斥锁是有效的,而另一个线程尝试使用 lock_guard 锁定它?

Dou*_*s B 0 c++ multithreading mutex pthreads stdthread

我为这个糟糕的标题道歉,只是想找人确认我没有疯。

我最近偶然发现了一些已经使用多年而没有人抱怨的代码。我调查它的原因是因为我在大量修改此代码库时遇到此错误:

pthread_mutex_lock.c:90: ___pthread_mutex_lock: Assertion `mutex->__data.__owner == 0' failed.
Run Code Online (Sandbox Code Playgroud)

经过一番挖掘,我发现互斥锁的使用(对我来说)非常奇怪。在本质上:

  1. 生成两个线程并与主线程分离(两个线程位于同一个 .cpp 文件中,并共享全局声明的互斥体)
  2. 根据评论,在似乎第一个被击中的线程中,互斥体被解锁,然后在循环中暂停 10us 来锁定,以“允许其他线程处理消息”
  3. 可能会第二次命中的线程(首先生成,但等待接收数据)在 switch 语句中有一个情况,该语句使用 a 锁定互斥体lock_guard

我相信这在很多方面都是未定义的行为,但我收到了一些反对意见,认为应该改变它,因为我正在努力一致且最小化地重现这个确切的错误。

我假设这是为了让互斥体首先被循环线程解锁(从解锁状态,线程还没有要lock_guard处理的数据),然后锁定,并且大部分时间都会被锁定,而lock_guard偶尔会尝试锁定,并且必须等待循环解锁它,此时它可以处理其数据,lock_guard超出范围,并且锁定返回到循环线程。

我认为导致我的错误的是;通过我的修改,线程lock_guard获取数据的速度比预期的要快,因此lock_guard触发了该情况,然后循环线程尝试解锁另一个线程互斥体。

我在寻找:

  • 确认是UB声明互斥量,然后解锁,不加锁
  • 如果循环线程在互斥锁被持有时解锁该互斥锁,则确认它是 UBlock_guard
  • 确认我对导致错误的原因的假设(我基本上已经从参考文献中知道了前两个,但想确保我没有错过一些我不知道的做事的大脑方式)
  • 也许有更好的方法来做到这一点,我认为这可以通过首先将互斥体锁定在循环之外来解决?

我搜索了代码库,互斥体只使用了 4 次,我可以看到:当它被声明时,当它被使用时,当lock_guard它被解锁时,然后被锁定,所以我的 MRE 很短,我认为它基本上有它需要的一切。演示如何使用该互斥锁:

#include <iostream>
#include <thread>
#include <mutex>
#include <unistd.h>

std::mutex data_mtx;

void lg_thread(){
  std::lock_guard<std::mutex> guard(data_mtx);

  usleep(10e6);
}

int main(int argc, char const* argv[]){
  std::thread t1(lg_thread);

  usleep(10000);

  for (int i = 0; i < 100; i++){
    data_mtx.unlock();
    usleep(500);
    data_mtx.lock();
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Rem*_*eau 5

我在寻找:

  • 确认是UB声明互斥量,然后解锁,不加锁

解锁mutex当前未由调用线程锁定/拥有的线程是未定义的行为,是的。

  • 如果循环线程在互斥锁被持有时解锁该互斥锁,则确认它是 UBlock_guard

是的,就是UB。往上看。

  • 确认我对导致错误的原因的假设(我基本上已经从参考文献中知道了前两个,但想确保我没有错过一些我不知道的做事的大脑方式)

往上看。

  • 也许有更好的方法来做到这一点

考虑使用 astd::condition_variable来协调线程。

我认为这可以通过首先将互斥体锁定在循环之外来解决?

是的。这样,如果main()锁定第mutex一个,则将lock_guard阻塞等待main()解锁它,如果lock_guard锁定第mutex一个,main()则将阻塞等待lock_guard解锁它。理应如此。