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
コマンドで確認できます。
スレッドとシグナルに対する C++ サポートライブラリの使用
C++ サポートライブラリ
libCrun
、libiostream
、libCstd
、およびlibC
は、マルチスレッドに対しては安全ですが、async
については安全ではありません。つまり、マルチスレッドアプリケーションでは、サポートライブラリにある関数でも、シグナルハンドラでは使用するべきではありません。使用すると、デッドロックが発生する可能性があります。マルチスレッドアプリケーションではシグナルハンドラで以下を使用することは、安全ではありません。
マルチスレッドプログラムでの例外の使用
現在の例外処理実装はマルチスレッド対応になっています。つまり、あるスレッド内の例外は、別のスレッドの例外を妨害することはありません。しかし、例外をスレッド間でやりとりすることはできません。つまり、あるスレッドが送出した例外を別のスレッドで受け取ることはできません。
terminate()
関数やunexpected()
関数は、各スレッドごとに独自のものを設定することができます。set_terminate()
やset_unexpected()
をあるスレッドで呼び出しても、その例外は呼び出し元のスレッドにだけ影響します。terminate()
がデフォルトで呼び出す関数は、対象がメインスレッドであればabort()
で、その他のスレッドであればthr_exit()
です。「実行時のエラーの指定」を参照してください。
注 - スレッドの取り消し (pthread_cancel
(3T)) を行うと、スタック上の自動 (局所的で非静的) オブジェクトが破棄されます。スレッドが取り消されると、局所的デストラクタの実行の前に、ユーザーがpthread_cleanup_push()
で登録した掃除用 (クリーンアップ) ルーチンが起動されます。特定のクリーンアップルーチンが登録された後に呼び出された関数の局所型オブジェクトは、ルーチンの実行前に破棄されます。
スレッド間での C++ 標準ライブラリオブジェクトの共有
C++ 標準ライブラリ (
libCstd
) はマルチスレッドに対して安全なので、ライブラリの内部はマルチスレッド環境で正しく動作します。しかし、自分で設定したスレッド間での共有ライブラリオブジェクトは、必ずロックしなければなりません (iostreams
とlocale
オブジェクトを除きます)。たとえば、文字列をインスタンス化すると、新しいスレッドを作成し、その文字列を参照によりスレッドに渡します。次に、その文字列への書き込みアクセスをロックしなければなりません。これは、スレッド間でその 1 つの文字列オブジェクトを明示的に共有しているからです (この作業を行うためにライブラリにより提供される機能については、次に説明します)。
一方、新しいスレッドに文字列を値で渡す場合、ロックについて考慮する必要はありません。これは、2 つの異なるスレッドにある文字列が Rogue Wave の「コピー・オン・ライト (書き込み時コピー)」技法により 1 つの表現を共有している場合も同じです。
ライブラリはこのロックを自動的に行います。自分でロックを行う必要があるのは、スレッド間での参照を渡したり、大域オブジェクトや静的オブジェクトを使うなどして、あるオブジェクトを複数のスレッドで明示的に使用可能にする場合だけです。
次に、C++ 標準ライブラリ内部で使用されているロック (同期) 機構について説明します。この機構によってスレッドが複数ある場合でも誤動作が発生しない仕組みになっています。
この機能へのインタフェース (ファイル、マクロ、クラス、クラスメンバーの名前を含む) は、実装同様、ライブラリ内部の詳細であるため、予告なしに変更される場合があります。後方互換性は保証されていません。
2 つの同期クラス
_RWSTDMutex
と_RWSTDGuard
が、マルチスレッド安全を実現するための機構を提供しています。
_RWSTDMutex
クラスは、次のメンバー関数により、プラットフォームに依存しないロック機構を提供しています。
void acquire()
-- 自分自身に対するロックを獲得するか、ロックが獲得できるまでブロックするvoid release()
--自分自身に対するロックを解放する
class _RWSTDMutex{public:_RWSTDMutex ();~_RWSTDMutex ();void acquire ();void release ();};
_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
マクロは空の式に展開されます。
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |