C++ プログラミングガイド ホーム目次前ページへ次ページへ索引


第 9 章

マルチスレッド化されたプログラム

この章ではマルチスレッド化されたプログラム (以降、マルチスレッドプログラムと呼ぶ) の作成方法について説明します。また、例外の使用方法と、スレッド間で C++ 標準ライブラリオブジェクトを共有する方法についても説明します。

マルチスレッドの詳細については、『マルチスレッドプログラミングガイド』、『C++ ライブラリ・リファレンス』、『Tools.h++ 7.0 ユーザーズガイド』、および『Standard C++ Library Class Reference』を参照してください。

マルチスレッドプログラムの構築

C++ コンパイラに付属されているライブラリはすべてマルチスレッドに対して安全です。マルチスレッド化されたアプリケーション (以降、マルチスレッドアプリケーションと呼ぶ) を作成したい場合、またはアプリケーションからマルチスレッド対応のライブラリにリンクしたい場合、-mt オプションを指定してプログラムをコンパイルおよびリンクしなければなりません。このオプションは、-D_REENTRANT をプリプロセッサに渡し、-lthread を正しい順序で ld に渡します。互換モード (-compat[=4]) の場合、-mt オプションを指定すると、libthread は必ず libC の前にリンクされます。標準モード (デフォルトモード) の場合、-mt オプションを指定すると、libthread は必ず libCrun の前にリンクされます。

-lthread を使ってアプリケーションを直接リンクしないでください。直接リンクすると、libthread が誤った順序でリンクされてしまいます。

コンパイルとリンクを別々の手順で行なった場合の、マルチスレッドアプリケーションの正しい作成方法の例を次に示します。

example% CC -c -mt myprog.cc
example% CC -mt myprog.o

次の例は、マルチスレッドアプリケーションの誤った作成方法です。

example% CC -c -mt myprog.o
example% CC myprog.o -lthread <- libthread が正しくリンクされない

マルチスレッド対応コンパイルの確認

アプリケーションが libthread にリンクされているかどうかは、ldd コマンドで確認できます。

example% CC -mt myprog.cc
example% ldd a.out
libm.so.1 =>      /usr/lib/libm.so.1
libCrun.so.1 =>   /usr/lib/libCrun.so.1
libw.so.1 =>      /usr/lib/libw.so.1
libthread.so.1 => /usr/lib/libthread.so.1
libc.so.1 =>      /usr/lib/libc.so.1
libdl.so.1 =>     /usr/lib/libdl.so.1

スレッドとシグナルに対する C++ サポートライブラリの使用

C++ サポートライブラリ libCrunlibiostreamlibCstd、および libC は、マルチスレッドに対しては安全ですが、async については安全ではありません。つまり、マルチスレッドアプリケーションでは、サポートライブラリにある関数でも、シグナルハンドラでは使用するべきではありません。使用すると、デッドロックが発生する可能性があります。

マルチスレッドアプリケーションではシグナルハンドラで以下を使用することは、安全ではありません。

マルチスレッドプログラムでの例外の使用

現在の例外処理実装はマルチスレッド対応になっています。つまり、あるスレッド内の例外は、別のスレッドの例外を妨害することはありません。しかし、例外をスレッド間でやりとりすることはできません。つまり、あるスレッドが送出した例外を別のスレッドで受け取ることはできません。

terminate() 関数や unexpected() 関数は、各スレッドごとに独自のものを設定することができます。set_terminate()set_unexpected() をあるスレッドで呼び出しても、その例外は呼び出し元のスレッドにだけ影響します。terminate() がデフォルトで呼び出す関数は、対象がメインスレッドであれば abort() で、その他のスレッドであれば thr_exit() です。「実行時のエラーの指定」を参照してください。


注 - スレッドの取り消し (pthread_cancel(3T)) を行うと、スタック上の自動 (局所的で非静的) オブジェクトが破棄されます。スレッドが取り消されると、局所的デストラクタの実行の前に、ユーザーが pthread_cleanup_push() で登録した掃除用 (クリーンアップ) ルーチンが起動されます。特定のクリーンアップルーチンが登録された後に呼び出された関数の局所型オブジェクトは、ルーチンの実行前に破棄されます。

スレッド間での C++ 標準ライブラリオブジェクトの共有

C++ 標準ライブラリ (libCstd) はマルチスレッドに対して安全なので、ライブラリの内部はマルチスレッド環境で正しく動作します。しかし、自分で設定したスレッド間での共有ライブラリオブジェクトは、必ずロックしなければなりません (iostreamslocale オブジェクトを除きます)。

たとえば、文字列をインスタンス化すると、新しいスレッドを作成し、その文字列を参照によりスレッドに渡します。次に、その文字列への書き込みアクセスをロックしなければなりません。これは、スレッド間でその 1 つの文字列オブジェクトを明示的に共有しているからです (この作業を行うためにライブラリにより提供される機能については、次に説明します)。

一方、新しいスレッドに文字列を値で渡す場合、ロックについて考慮する必要はありません。これは、2 つの異なるスレッドにある文字列が Rogue Wave の「コピー・オン・ライト (書き込み時コピー)」技法により 1 つの表現を共有している場合も同じです。

ライブラリはこのロックを自動的に行います。自分でロックを行う必要があるのは、スレッド間での参照を渡したり、大域オブジェクトや静的オブジェクトを使うなどして、あるオブジェクトを複数のスレッドで明示的に使用可能にする場合だけです。

次に、C++ 標準ライブラリ内部で使用されているロック (同期) 機構について説明します。この機構によってスレッドが複数ある場合でも誤動作が発生しない仕組みになっています。

この機能へのインタフェース (ファイル、マクロ、クラス、クラスメンバーの名前を含む) は、実装同様、ライブラリ内部の詳細であるため、予告なしに変更される場合があります。後方互換性は保証されていません。

2 つの同期クラス _RWSTDMutex_RWSTDGuard が、マルチスレッド安全を実現するための機構を提供しています。

_RWSTDMutex クラスは、次のメンバー関数により、プラットフォームに依存しないロック機構を提供しています。

_RWSTDGuard クラスは、_RWSTDMutex クラスのオブジェクトをカプセル化する、便利なラッパークラスです。_RWSTDGuard オブジェクトは、自分自身のコンストラクタ中でカプセル化された相互排他ロック (mutex) を獲得しようと試み (エラー時にスローされる std::exception から派生した ::thread_error 型の例外をスローし)、デストラクタで相互排他ロックを解放します (デストラクタは例外をスローしない)。

class _RWSTDGuard
{
public:
    _RWSTDGuard (_RWSTDMutex&);
    ~_RWSTDGuard ();
};

また、マクロ _RWSTD_MT_GUARD (mutex) (以前の _STDGUARD) を使用すると、マルチスレッドアプリケーションを構築する際に _RWSTDGuard クラスオブジェクトの生成を条件によって有効または無効にできます。_RWSTDGuard オブジェクトは、自分が定義されているブロック中のコードが、複数のスレッドによって同時に実行されないように保護します。スレッドが 1 つしかない場合は、_RWSTD_MT_GUARD マクロは空の式に展開されます。

次に、これらの機構の使用例を示します。

#include <rw/stdmutex.h>;

 
//
// 複数のスレッドで共有される整数
//
int I;

 
//
// I への更新を同期させるために使用する相互排他ロック(mutex)
//
_RWSTDMutex I_mutex;

 
//
// I を 1 増やす。_RWSTDMutex を直接使用する
//

 
void increment_I ()
{
   I_mutex.acquire();  // mutex をロックする
   I++;
   I_mutex.release();  // mutex のロックを解放する
}

 
//
// I を 1 減らす。_RWSTDGuard を使用する
//

 
void decrement_I ()
{
   _RWSTDGuard guard(I_mutex);  // I_mutex に対するロックを獲得する
   --I;
   //
   // I に対するロックは、guard デストラクタが呼び出されたときに解放される
   //
}


サン・マイクロシステムズ株式会社
Copyright information. All rights reserved.
ホーム   |   目次   |   前ページへ   |   次ページへ   |   索引