スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

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

MSDNに昔からある古い説明がある。
俺は、紛らわしい、嘘、判り難いなどと考えている。

古いまま直されていないから仕方ないんだけど誤解が多いからな。
とりあえず全文を載せる。全文だと引用ではなくなってしまうのだが、URLだと変わることがあるので。
マルチスレッド : プログラミングのヒント

マルチスレッド アプリケーションでデータにアクセスするときは、シングルスレッドの場合より慎重に行う必要があります。マルチスレッドアプリケーションでは、複数のスレッドがそれぞれ個別に同時に実行されるので、アルゴリズムやデータに関して、複数のスレッドで同じデータが使われることを考慮する必要があります。ここでは、MFC (Microsoft Foundation Class) ライブラリを使ってマルチスレッドアプリケーションを開発するときに、このような問題を回避する手法について説明します。

* 複数のスレッドからのオブジェクトへのアクセス
* 非 MFC スレッドからの MFC オブジェクトのアクセス
* ウィンドウ ハンドルのマップ
* スレッド間通信

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

サイズおよびパフォーマンス上の理由により、MFC オブジェクトのスレッドの安全性は、オブジェクト レベルでは保証されず、クラス レベルでしか保証されません。つまり、2 つの異なるスレッドで 2 つの異なる CString オブジェクトを操作することはできますが、2 つの異なるスレッドで同じ CString オブジェクトを操作することはできません。複数のスレッドで 1 つのオブジェクトを操作する必要があるときは、Win32 のクリティカルセクションなどの同期機構を使ってアクセスを保護します。クリティカル セクションなどの関連オブジェクトについては、『Platform SDK』の「Synchronization」を参照してください。

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

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

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

ウィンドウ ハンドルのマップ

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

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

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

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

スレッド間通信

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



以降、それぞれについてコメントしていく。

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

これに関してはその通りだ。

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

これに関してもその通りだ。
Win32APIでスレッドを作成すると、C言語用のライブラリやMFCのスレッド用の初期化が行われないので、一部都合が悪いのがでてくるのだ。

ウィンドウ ハンドルのマップ

これに関しては言いたいことがたくさんある。

UpdateAllViewsについては仕組みを追ったことはないので判らない。

掲示板などの発言で見かけて気になる
CMyWnd(CWndやCDialogなどの自分が作った派生クラス)へのポインタをスレッドに渡してもスレッドローカルストレージ(TLS)だからアクセスできません。
というもの。これは私が脚色している。

CMyWndそのものは、単なる変数だったりnew CMyWndで作ったものだったりして、TLSでは無い。
それにTLSであっても、ポインタを渡せば別のスレッドからアクセスできてしまう。

MSDNの解決策で
FromHandle メンバ関数を呼び出して、一時マップにオブジェクトを登録します。
関数 Attach では、オブジェクトをスレッドのパーマネント マップに登録できます。
とあるが
これって、CMyWnd*じゃなくて偽のCWndが作られてスレッドのパーマネントマップに登録してんだよな。

登録されてないと困るメンバ関数なんてある?
後で探してみるか。

登録されて何が解決する?

2 つのスレッドが同じアドレス空間を共有する点が異なります。
とあるが
同じプロセスの全スレッドは1つのアドレス空間だけど。
同じプロセスなのにスレッドでアドレス空間が違うことなんてないでしょ。

ちゃんと説明しようとすると、もっと長い文章になってしまうな。

CMyWndポインタを渡すことは出来るよ。
マルチスレッドであることに注意せず使い方間違えればそりゃ問題だよ。
PostMessageは非同期であることに注意。
SendMessageは相手スレッドで実行されるのを待つのでデッドロックに注意。
別スレッドのウィンドウ操作にかかわる処理は危険。スレッドセーフじゃなかったり、SendMessageと同じ理由。

マルチスレッド関連って微妙なタイミングでおかしい動作になるから、危険か安全化を確かめるのが難しいんだよな。
俺のここの発言も、正しいか正しくないか上手く確認できない。

話が長くなりすぎて、ブログの編集がし辛いので
この件は一旦おわりにします。
  1. 2007/01/22(月) 22:30:17|
  2. Win/C++
  3. | トラックバック:0
  4. | コメント:1
<<ホームページビルダでブログ更新 | ホーム | ソースをHTMLにする>>

コメント

「2 つのスレッドが同じアドレス空間を共有する点が異なります。」は「この通信方法は、2 つの異なるアプリケーション間の対話動作に似ています。ただし、2 つのスレッドが同じアドレス空間を共有する点が異なります。」をひとかたまりで読むものかと。

つまり、FramHandleを使う方法との違いを言っているのではなく、プロセス間のメッセージのやり取りとの違いを言っている、のかと思います。
  1. 2007/08/07(火) 17:07:52 |
  2. URL |
  3. d #-
  4. [ 編集]

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://wclrp.blog90.fc2.com/tb.php/4-97dd2e04
この記事にトラックバックする(FC2ブログユーザー)
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。