マルチスレッドのプログラミング

ロック階層の使用

同時に 2 つの資源をアクセスすることがあります。一方の資源を使用しているとき、もう一方の資源も必要となる場合があります。例 4-2 は、2 つのスレッドが同じ 2 つの資源を要求しようとして両者が異なる順序で、対応する相互排他ロックを獲得しようとする場合に問題が生じることを示しています。この例では 、2 つのスレッドがそれぞれ mutex の 1 と 2 をロックした場合、次に各スレッドが互いにもう一方の mutex をロックしようとするとデッドロックが発生します。


例 4-2 デッドロック

スレッド 1 

スレッド 2 

pthread_mutex_lock(&m1);

 

/* 資源 1 を使用 */ 

 

pthread_mutex_lock(&m2);

 

/* 資源 1 と 2 を使用 */ 

 

pthread_mutex_unlock(&m2);

pthread_mutex_unlock(&m1);

pthread_mutex_lock(&m2);

 

/* 資源 2 を使用 */ 

 

pthread_mutex_lock(&m1);

 

/* 資源 1 と 2 を使用 */ 

pthread_mutex_unlock(&m1);

pthread_mutex_unlock(&m2);


この問題を回避する最善の方法は、スレッドで複数の mutex をロックする場合、常に同じ順序でロックすることです。この方法をロック階層と呼び、mutex に論理的な番号を割り振ることにより mutex に順序を付けます。

自分がある番号を持つ mutex を保持しているときより小さい番号が割り振られている mutex はロックできないという規定を守るようにします。


注 -

ロック lint ツールを使うと、この例で示したようなデッドロックの問題を検出できます。この種のデッドロック問題を回避する最善の方法は、ロック階層を使用することです。常に一定の順序でロックする限り、デッドロックは発生しません。


ただし、この方法は常に使用できるとは限りません。規定と違う順序で相互排他ロックを獲得しなければならないこともあるからです。そのような状況でデッドロックを防ぐには、pthread_mutex_trylock() を使用します。デッドロックが避けられないような事態が生じた場合は、ある 1 つのスレッドが現在保持している mutex のロックを解除する必要があります。

例 4-3 は、その方法を示しています。


例 4-3 条件付きロック

スレッド 1 

スレッド 2 

pthread_mutex_lock(&m1); pthread_mutex_lock(&m2);

 

 

 

 

/* 解放 */ 

 

pthread_mutex_unlock(&m2);

pthread_mutex_unlock(&m1);

for (; ;) 

{ pthread_mutex_lock(&m2);

 

if (pthread_mutex_trylock(&m1)==0)

/* 獲得成功 */  

break;

/* 獲得失敗 */ 

pthread_mutex_unlock(&m2);

/* ロックを獲得し、解放 */ 

pthread_mutex_unlock(&m1);

pthread_mutex_unlock(&m2);


この例では、スレッド 1 は mutex を規定通りの順序でロックしようとしていますが、スレッド 2 ではロックの順序が違います。デッドロックが発生しないようにするために、スレッド 2 は mutex の 1 を慎重にロックしなければなりません。これは、mutex の 1 が解放されるまで待つとすると、スレッド 1 との間にデッドロックの関係が生じる恐れがあるからです。

これを防ぐため、スレッド 2 は pthread_mutex_trylock() を呼び出し、mutex がロックされていなければロックします。ロックされていれば、スレッド 2 はただちにエラーを返します。その時点で、スレッド 2 は mutex の 2 を解放しなければなりません。その結果、スレッド 1 は mutex の 2 をロックでき、最終的には mutex の 1 と 2 の両方を解放します。