以下に、マルチスレッドプログラミングでよく起こるミスを示します。
呼び出し側のスタックへのポインタを新しいスレッドの引数として渡す。
2 つのスレッドが異なる順序で、同じ組の広域リソースへの権利を獲得しようとしてデッドロックが発生する。この場合は、一方のスレッドが最初のリソースを獲得し、もう一方のスレッドが 2 番目のリソースを獲得し、どちらかがリソースを放棄するまで処理が進まなくなります。
すでに保持しているロックを獲得しようとする (再帰的なデッドロック)。
同期機構の安全性に見えない間隙が生じている。これは、同期機構によって保護されているプログラム内で同期機構をいったん解除し、再度獲得してから戻る関数を呼び出していることが原因です。関数の呼び出し側から見ると広域データが保護されているようでも、実際には保護されていません。
UNIX のシグナルとスレッドを組み合わせて使っている。非同期的なシグナルの処理には sigwait(2) を使用するほうがよいでしょう。
setjmp(3B) と longjmp(3B) を使用し、相互排他ロックを解放せずにロングジャンプする。
*_cond_wait(3T) または *_cond_timedwait(3T) の呼び出しから復帰した後、条件の再評価に失敗した。
デフォルトスレッドを PTHREAD_CREATE_JOINABLE として生成した場合は、その記憶領域を pthread_join(3T) で再利用しなければならないことを忘れている。なお、pthread_exit(3T) は記憶領域を解放しません。
スタックの大きさの指定が適切でないか、デフォルト以外のスタックを使用している。
次の点にも注意してください。マルチスレッドプログラムの動きは、特にバグがある場合には、同じ入力で続けて実行しても再現性がないことがよくあります。これは、スレッドのスケジューリングの順序が定まっていないからです。
一般にマルチスレッドプログラムのバグは、決定的というよりも統計的な発生傾向を示します。このため実行レベルの問題を見つけるには、ブレークポイントによるデバッグよりもトレースの方が有効です。
TNF ユーティリティ (Solaris システムの一部) は、アプリケーションとライブラリからの性能解析情報の収集、追跡、デバッグに使用します。TNF ユーティリティは、カーネルおよび複数のユーザプロセスとスレッドからの追跡情報を集約するので、マルチスレッドコードに特に有用です。
TNF ユーティリティを使用すると、マルチスレッドプログラムの追跡とデバッグが容易になります。
システムコールとシグナルの追跡については、truss(1) を参照してください。
マルチスレッドプログラム内ですべてのスレッドを結合するときは、スレッドと LWP とは同義になります。その場合は、マルチスレッドプログラミングをサポートする以下の adb コマンドを用いて、各スレッドにアクセスできます。
表 7-3 マルチスレッド対応の adb コマンド
pid:A |
pid で指定したプロセスに接続する。プロセスと、そのすべての LWP は停止する。 |
:R |
プロセスから切り離す。プロセスと、そのすべての LWP は再開される。 |
$L |
(停止した) プロセス内の有効な LWP を一覧表示する。 |
n:l |
フォーカスを n で指定した LWP に切り替える。 |
$l |
現在のフォーカスの LWP を表示する。 |
num:i |
num で指定したシグナルを無視する。 |
以下のコマンドは、条件付きブレークポイントを設定するためによく使用されます。
表 7-4 adb ブレークポイントの設定
[label],[count]:b [expression] |
expression の評価結果が 0 のときにブレークポイントにヒットする。 |
foo,ffff:b <g7-0xabcdef |
g7 = 0xABCDEF (16 進数値) のときに foo で停止する。 |
dbx ユーティリティでは、C++、ANSI C、FORTRAN のソースプログラムをデバッグしたり、実行したりできます。dbx のコマンドは、 デバッガと同じコマンドを受けつけますが、標準端末 (tty) インタフェースを使用する点が異なります。dbx とデバッガのどちらも、現在はマルチスレッドプログラムのデバッグをサポートしています。dbx とデバッガの詳細は、dbx(1) のマニュアルページおよび『Sun WorkShop 入門』 を参照してください。
以下に示す表 7-5 にある dbx のオプションは、すべてマルチスレッドアプリケーションをサポートできます。
表 7-5 dbx のマルチスレッドプログラム用オプション