4

我有一个在 Linux 上运行的多线程程序,有时如果我对它运行 gstack,有一个线程等待锁定很长时间(比如 2-3 分钟),

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

0 0x40000410 在 __kernel_vsyscall ()

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

2 0x40010e1d in _L_lock_981 () from /lib/i686/nosegneg/libpthread.so.0

3 0x40010d3b 在来自 /lib/i686/nosegneg/libpthread.so.0 的 pthread_mutex_lock ()

...

我检查了其余的线程,它们都没有获得这个锁,但是,过了一会儿,这个线程(LWP 19853)可以成功地获得这个锁。

应该有一个线程已经获得了这个锁,但是我没有找到它,我错过了什么吗?

编辑: pthread_mutex_t 的定义:

类型定义联合

{

结构 __pthread_mutex_s {

int __lock;

无符号整数__count;

int __owner;

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

int __kind;

无符号整数 __nuers;

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

} __数据;

字符_大小[ _SIZEOF_PTHREAD_MUTEX_T];

长整数 __align;

} pthread_mutex_t;

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

4

4 回答 4

2

2-3 分钟听起来很多,但是如果您的系统负载很重,则无法保证您的线程在另一个线程解锁互斥锁后立即唤醒。因此,在您查看它的那一刻,可能没有线程(不再)持有锁。

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

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

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

于 2012-07-09T09:47:16.667 回答
2

Mutexes by default don't track the thread that locked them. (Or at least I don't know of such a thing )

There are two ways to debug this kind of problem. One way is to log every lock and unlock. On every thread creation you log the value of the thread id that got created. Right after locking any lock, you log the thread id, and the name of the lock that was locked ( you can use file/line for this, or assign a name to each lock). And you log again right before unlocking any lock.

This is a fine way to do it if your program doesn't have tens of threads or more. After that the logs start to become unmanageable.

The other way is to wrap your lock in a class that stores the thread id in a lock object right after each lock. You might even create a global lock registry that tracks this, that you can print out when you need to.

Something like:

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

The key here is - don't implement either of these methods for your release version. Both a global locking log, or a global registry of lock states creates a single global resource that will itself become a resource under lock contention.

于 2012-07-09T13:10:46.000 回答
0

POSIX API 不包含执行此操作的函数。

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

于 2012-07-09T08:53:02.340 回答
0

对于此类调试问题,您可能会在程序中添加两个特殊的日志记录调用,说明哪个线程何时获得锁以及何时返回锁。

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

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

于 2012-07-09T12:46:09.827 回答