マルチスレッド: MFC プログラミングのヒント

マルチスレッド アプリケーションでは、操作が意図した順序で行われ、複数のスレッドによってアクセスされるデータが破損していないことを確認するために、シングル スレッド アプリケーションよりも厳格に対処する必要があります。 このトピックでは、MFC (Microsoft Foundation Class) ライブラリを使用してマルチスレッド アプリケーションを開発する場合にこのような問題を回避する手法について説明します。

複数のスレッドからオブジェクトにアクセスする

MFC オブジェクトは、それ自体ではスレッド セーフではありません。 MFC 同期クラスや、重要なセクションなどの適切な Win32 同期オブジェクトを使用しない限り、2 つの個別のスレッドで同じオブジェクトを操作することはできません。 クリティカル セクションやその他の関連オブジェクトの詳細については、Windows SDK の「Synchronization」を参照してください。

クラス ライブラリは内部的にクリティカル セクションを使って、デバッグ メモリの割り当てなどが使うグローバル データ構造を保護しています。

非 MFC スレッドからの MFC オブジェクトへのアクセス

CWinThread オブジェクトを使わずにスレッドを作成したマルチスレッド アプリケーションでは、作成したスレッドから MFC オブジェクトにアクセスできません。 つまり、MFC オブジェクトにセカンダリ スレッドからアクセスするには、「マルチスレッド : ユーザー インターフェイス スレッドの生成」または「マルチスレッド : ワーカー スレッドの生成」で説明した方法のいずれかを使用してセカンダリ スレッドを作成する必要があります。 ほかの方法では、マルチスレッド アプリケーションの実行に必要な内部変数をクラス ライブラリで初期化できません。

Windows ハンドル マップ

通常、スレッドは自分自身が作成した MFC オブジェクトしかアクセスできません。 これは、Windows ハンドルの一時マップとパーマネント マップがスレッド ローカル ストレージに保持されており、複数のスレッドから同時にアクセスできないためです。 たとえば、あるワーカー スレッドで計算を行ってから、ドキュメント オブジェクトの UpdateAllViewsメンバー関数を呼び出して、新しいデータに対するビューの格納先ウィンドウに反映させることはできません。 CWnd オブジェクトから HWND へのマップはプライマリ スレッドに固有であるからです。 つまり、あるスレッドで Windows ハンドルから C++ オブジェクトへのマップが行われるときに、別のスレッドで同じハンドルを別の C++ オブジェクトにマップが行われることもあります。 あるスレッドで行われた変更は、ほかのスレッドには反映されません。

この問題を解決する方法はいくつかあります。 第 1 の方法は、ワーカー スレッドに C++ オブジェクトを渡さずに HWND などのハンドルを個別に渡す方法です。 ワーカー スレッドは該当する FromHandle メンバー関数を呼び出して、一時マップにオブジェクトを登録します。 Attach を呼び出すことで、オブジェクトをスレッドのパーマネント マップに登録できますが、この方法は、オブジェクトがスレッドの消滅後も残存する場合以外は使わないでください。

もう 1 つの方法は、ワーカー スレッドが処理するタスクごとに新しいユーザー定義のメッセージを作成し、このメッセージを ::PostMessage 関数を使ってアプリケーションのメイン ウィンドウに渡す方法です。 この通信方法は、2 つの異なるアプリケーション間の対話動作に似ています。ただし、2 つのスレッドが同じアドレス空間を共有する点が異なります。

ハンドルのマップの詳細については、「テクニカル ノート 3」を参照してください。 スレッド ローカル ストレージの詳細については、Windows SDK の「Thread Local Storage」と「Using Thread Local Storage」を参照してください。

スレッド間の通信

MFC には、スレッドからオブジェクトへのアクセスを同期化して、スレッドの安全性を保証するクラスがあります。 これらのクラスの使い方については、「マルチスレッド : 同期クラスの使用法」と「マルチスレッド : 同期クラスの使い分け」を参照してください。 これらのオブジェクトの詳細については、Windows SDK の「Synchronization」を参照してください。

関連項目

C++ と MFC を使用するマルチスレッド