4

Linux で実行されているマルチスレッド プログラムがあります。それに対して gstack を実行すると、ロックを長時間 (たとえば 2 ~ 3 分) 待機しているスレッドが存在することがあります。

スレッド 2 (スレッド 0x5e502b90 (LWP 19853)):

0 0x40000410 in __kernel_vsyscall ()

/lib/i686/nosegneg/libpthread.so.0 からの __lll_lock_wait () の 1 0x400157b9

/lib/i686/nosegneg/libpthread.so.0 からの _L_lock_981 () の 2 0x40010e1d

/lib/i686/nosegneg/libpthread.so.0 からの pthread_mutex_lock () の 3 0x40010d3b

...

残りのスレッドをチェックしたところ、いずれもこのロックを取得していませんでしたが、しばらくすると、このスレッド (LWP 19853) がこのロックを正常に取得できました。

このロックを既に取得しているスレッドが 1 つ存在するはずですが、見つかりませんでした。足りないものはありますか?

編集: pthread_mutex_t の定義:

型定義ユニオン

{

struct __pthread_mutex_s {

int __ロック;

unsigned int __count;

int __所有者;

/* バイナリ互換性を維持するために、KIND は構造内のこの位置にとどまる必要があります。*/

int __種類;

unsigned int __nusers;

拡張ユニオン { int __spins; __pthread_slist_t __list; };

} __データ;

char _ size[ _SIZEOF_PTHREAD_MUTEX_T];

long int __align;

} pthread_mutex_t;

メンバー「__owner」があり、現在ミューテックスを保持しているスレッドのIDです。

4

4 に答える 4

2

デフォルトでは、ミューテックスはそれらをロックしたスレッドを追跡しません。(または少なくとも私はそのようなことを知りません)

この種の問題をデバッグする方法は2つあります。1つの方法は、すべてのロックをログに記録してロックを解除することです。スレッドを作成するたびに、作成されたスレッドIDの値をログに記録します。ロックをロックした直後に、スレッドIDとロックされたロックの名前をログに記録します(これにはファイル/行を使用するか、各ロックに名前を割り当てることができます)。そして、ロックを解除する直前に再度ログに記録します。

これは、プログラムに数十以上のスレッドがない場合にこれを行うための優れた方法です。その後、ログは管理不能になり始めます。

もう1つの方法は、各ロックの直後にスレッドIDをロックオブジェクトに格納するクラスでロックをラップすることです。これを追跡するグローバルロックレジストリを作成して、必要なときに印刷できるようにすることもできます。

何かのようなもの:

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

ここで重要なのは、リリースバージョンにこれらのメソッドのいずれも実装しないことです。グローバルロックログまたはロック状態のグローバルレジストリの両方が単一のグローバルリソースを作成し、それ自体がロック競合中のリソースになります。

于 2012-07-09T13:10:46.000 に答える
2

2 ~ 3 分というと長く聞こえますが、システムの負荷が高い場合、別のスレッドがミューテックスのロックを解除した直後にスレッドがウェイクアップするという保証はありません。そのため、見ている瞬間にロックを保持しているスレッドが (もう) 存在しない可能性があります。

Linux ミューテックスは 2 段階で機能します。だいたい:

  • 最初の段階ではint、ミューテックスをすぐにロックできるかどうかを確認するために、値に対してアトミック CAS 操作が行われます。
  • これが不可能な場合futex_waitは、同じアドレスのシステム コールがintカーネルに渡されます。

ロック解除操作は、値を初期値 (通常は0) に戻し、futex_wakeシステム コールを実行することで構成されます。次に、カーネルは誰かfutex_waitが同じアドレスで呼び出しを登録したかどうかを調べ、それらのスレッドをスケジューリング キューに復活させます。実際にどのスレッドがいつ起動するかは、特に有効になっているスケジューリング ポリシーなど、さまざまなことに依存します。スレッドが配置した順序でロックを取得するという保証はありません。

于 2012-07-09T09:47:16.667 に答える
0

このようなデバッグの問題については、プログラムに特別なログ呼び出しを 2 つ追加して、どのスレッドがロックを取得したか、いつロックを返したかを示すことができます。

このようなログ エントリは、最後にロックを取得したスレッドを見つけるのに役立ちます。

とにかくそうすると、プログラムの実行時の動作が大幅に変更される可能性があり、デバッグ対象の問題は、マルチスレッドアプリケーションでよく見られるような古典的なハイゼンバグのようなものとして現れることはなくなります。

于 2012-07-09T12:46:09.827 に答える
0

POSIX API には、それを行う関数は含まれていません。

一部のプラットフォームでは、実装によってこれが許可されない可能性もあります。
たとえば、ロックでは、ロック時に 1 に設定されたアトミック変数を使用できます。それを取得するスレッドはその ID をどこにも書き込む必要がないため、どの関数も ID を見つけることができません。

于 2012-07-09T08:53:02.340 に答える