Linux:我如何find持有特定锁的线程?

我有一个在Linux上运行的multithreading程序,有时如果我运行gstack反对它,有一个线程很长一段时间(例如2-3分钟)等待locking,

线程2(线程0x5e502b90(LWP 19853)):

__kernel_vsyscall()中的0x40000410

1 __lll_lock_wait()中的0x400157b9来自/lib/i686/nosegneg/libpthread.so.0

_L_lock_981()中的2 0x40010e1d来自/lib/i686/nosegneg/libpthread.so.0

3 0x40010d3b在pthread_mutex_lock()从/lib/i686/nosegneg/libpthread.so.0

我检查了其余的线程,他们都没有采取这个锁,但是,一段时间后,这个线程(LWP 19853)可以成功获得这个锁。

应该有一个已经获得这个锁的线程,但我没有find它,有什么我失踪?

编辑:pthread_mutex_t的定义:

typedef联合

{

struct __pthread_mutex_s {

int __lock;

unsigned int __count;

int __owner;

/ * KIND必须留在结构中的这个位置以保持二进制兼容性。 * /

int __kind;

unsigned int __nusers;

扩展联合{int __spins; __pthread_slist_t __list; };

} __data;

char _ size [ _SIZEOF_PTHREAD_MUTEX_T];

long int __align;

} pthread_mutex_t;

有一个成员“__owner”,它是现在持有互斥体的线程的id。

2-3分钟听起来很多,但是如果你的系统负载很重,就不能保证你的线程在另一个解锁了互斥之后立即醒来。 所以在你看的时候可能没有线程(已经存在)。

Linux互斥体分两个阶段工作。 大致:

  • 在第一阶段,对int值有一个原子CAS操作来查看互斥锁是否可以立即被锁定。
  • 如果这是不可能的, futex_wait具有相同int地址的futex_wait系统调用传递给内核。

解锁操作包括将值更改回初始值(通常为0 )并执行futex_wake系统调用。 内核然后看看是否有人在同一地址上注册了futex_wait调用,并在调度队列中恢复这些线程。 哪个线程真的被唤醒,何时取决于不同的事情,特别是启用的调度策略。 不能保证线程按照它们放置的顺序获得锁。

互斥锁默认情况下不会跟踪锁定它们的线程。 (或者至少我不知道这样的事情)

有两种方法来调试这种问题。 一种方法是记录每个锁并解锁。 在每一个线程创建你记录创建的线程ID的值。 在锁定任何锁之后,您将记录线程ID以及锁定的锁的名称(可以使用文件/行或为每个锁分配一个名称)。 在解锁任何锁之前,您再次登录。

如果您的程序没有数十个线程或更多,这是一个很好的方法。 之后,日志开始变得难以管理。

另一种方法是将你的锁包装在一个类中,该类在每次锁定之后将线程ID存储在一个锁定对象中。 你甚至可以创建一个跟踪这个的全局锁定注册表,你可以在需要的时候打印出来。

就像是:

 class MyMutex { public: void lock() { mMutex.lock(); mLockingThread = getThreadId(); } void unlock() { mLockingThread = 0; mMutex.unlock(); } SystemMutex mMutex; ThreadId mLockingThread; }; 

这里的关键是 – 不要为你的发行版本实现这些方法。 全局锁定日志或锁定状态的全局注册表都会创建单个全局资源,该资源本身将成为锁争用下的资源。

POSIX API不包含这样做的函数。

也有可能在某些平台上,实现不允许这样做。
例如,一个锁可以使用一个原子变量,锁定时设置为1。 获取它的线程不需要在任何地方写它的ID,所以没有函数可以找到它。

对于这样的调试问题,你可能会给程序添加特殊的日志记录调用,说明何时已经获得了锁的时间以及何时将其返回。

这样的日志条目将帮助您找到哪个线程最后获得了锁。

无论如何,这样做可能会大大地改变程序的运行时间行为,并且调试的问题不会像多线程应用程序中经常看到的那样出现在经典的heisenbug中。