繁体   English   中英

如何调试读写锁死锁?

[英]How do I debug a deadlock of readers-writer locks?

我正在编写一个程序,它有一个线程将点文件读入缓冲区,许多线程从缓冲区中获取点并构造它们的八叉树。 八叉树的每个多维数据集都由一个读写锁(又名 shared_mutex)保护,其中有 67 个(如果有两个线程,现在有)。 如果文件太大,程序就会死锁,我很难调试它。 其中一个锁在 gdb 中如下所示:

[6] = {_M_impl = {_M_rwlock = {__data = {__readers = 1, 
          __writers = 0, __wrphase_futex = 1, __writers_futex = 0, __pad3 = 0, 
          __pad4 = 0, __cur_writer = 0, __shared = 0, __rwelision = 0 '\000', 
          __pad1 = "\000\000\000\000\000\000", __pad2 = 0, __flags = 0}, 
        __size = "\001\000\000\000\000\000\000\000\001", '\000' <repeats 46 times>, __align = 1}}},

大多数互斥体的 __readers=1,一个有 __readers=3,一个有 __readers=4294967289 左右。 这是没有意义的,因为只有两个线程,所以只有两个线程可以读取它们; 在构建八叉树阶段,他们应该是写锁定而不是读锁定互斥锁,并且 -7 看起来像七个线程已经读解锁了互斥锁而没有先读锁定它。 尝试在 __readers 上设置观察点不起作用; 它使调试器崩溃,或类似的东西。

我为锁定和解锁写了一个包装器:

void lockBlockR(int block)
{
  metaMutex.lock();
  modReaders[block%modMutexSize]++;
  metaMutex.unlock();
  modMutex[block%modMutexSize].lock_shared();
}

void lockBlockW(int block)
{
  modMutex[block%modMutexSize].lock();
}

void unlockBlockR(int block)
{
  metaMutex.lock();
  if (--modReaders[block%modMutexSize]<0)
    cout<<"Read-unlocked "<<block<<" too many times\n";
  metaMutex.unlock();
  modMutex[block%modMutexSize].unlock_shared();
}

void unlockBlockW(int block)
{
  modMutex[block%modMutexSize].unlock();
}

当程序挂起时,我查看了 modReaders 并且全为零,然后查看 modMutex 并且它再次具有大部分 __readers=1 和一个负数。 我如何弄清楚发生了什么?

我正在运行 Eoan Ermine、Linux 5.3.0 和 libc 2.30。 该程序使用C++17中的gcc 9.2.1编译。

我以前在 PerfectTIN ( https://github.com/phma/perfecttin ) 中使用了读写器锁和模锁池,但是模池中的锁是普通的互斥锁。

ETA:我添加了另一个名为 modWriters 的整数modWriters和一些调试语句,并在解锁未锁定的互斥锁时捕获了一个线程。 但是,它是写锁定和写解锁,所以这并不能解释为什么__readers搞砸了。

如何调试读写锁死锁?

Consider using valgrind , GCC 10static analysis options, andinstrumentation options such as -fsanitize=thread , and Clang static analyzer .

从它的源代码构建GCC 10是值得的。

请注意,并非总是可以静态可靠地检测所有死锁(赖斯定理)。 阅读这份报告草稿 你可以有heisenbugs

可能使用 C++线程库,特别是std::lock_guard

你可能更喜欢std::recursive_mutexstd::mutex ,即使递归互斥体更慢更重(有些人说应该避免)。 我的观点是,它们通常更安全。

您可以考虑使用POCOQtGtkMM库的多线程功能。

请注意futex(7) ,Linux 上的基本锁定块。 您可以使用strace(1) (和pipe(7)进行线程间通信或与poll(2)同步;另请参见eventfd(2)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM