C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[6]: MFC マルチスレッドについて


(過去ログ 13 を表示中)

[トピック内 30 記事 (1 - 20 表示)]  << 0 | 1 >>

■3776 / inTopicNo.1)  MFC マルチスレッドについて
  
□投稿者/ Jitta (329回)-(2007/05/23(Wed) 12:12:44)

分類:[C/C++] 

 こんにちは、Jittaです。MFC のマルチ スレッドについて、教えてください。

 UI を持つスレッドから、通信スレッドを起動しています*1。このとき、通信スレッドに CResizingDialog を継承したダイアログ クラスのオブジェクトを引き渡しています。
通信スレッドでは、通信の結果を‘ダイアログにあるリスト ビューへ書き込んでいます’。
また、‘ダイアログの公開フィールドにある、CObjArray を操作しています’。

 MFC の知識がほとんど無いので尋ねたいのですが、
これらの(引用符で囲った)操作は、スレッド セーフでしょうか。


疑問に思う理由:
 .NET Framework では、Invoke メソッドを利用して、コントロールのもとになるウインドウ ハンドルを持つスレッドで行わなければならない。
MFC では、「ワーカー スレッド」と「ユーザー インターフェース スレッド」があることがわかった。このうち、「ユーザー インターフェース スレッド」を使用している*1。
このように分けてあるからには、「ユーザー インターフェース スレッド」では、同期をとるような仕掛けをしてくれているのかな?
 CSyncObject(の派生クラス)は、コードを見ている感じでは使っていません。


*1:AfxBeginThread 関数の第1引数が「CWinThread の派生クラスのオブジェクトの RUNTIME_CLASS」である。


 MSDN の「C++ と MFC を使用するマルチ スレッド」は、一通り読みました。直さなきゃいけないような気がする。。。

引用返信 編集キー/
■3794 / inTopicNo.2)  Re[1]: MFC マルチスレッドについて
□投稿者/ とっちゃん (125回)-(2007/05/23(Wed) 21:20:06)
とっちゃん さんの Web サイト
No3776 (Jitta さん) に返信
#フィードとれてなくて気がつかなかった。

まずは、MFC側。

おおざっぱですが、CObArray 以外は大丈夫です。

MFC の内部でスレッド固有のデータを持っているのは、FromHandlePermanent() と
FromHandle() の2つのメソッドです。(CWnd, CGdiObject, CImageListなどにあります)。
それ以外は、スレッド固有情報は持っていません。

この点で気になるのは、スレッド側で上記二つのメンバーを呼び出している個所がないか?
ですね。なければ問題はありません。

類似の注意点として、CWinThread の派生クラスを取得するグローバル関数も同様です。
こちらも、スレッド側で AfxGetApp() が呼ばれると NULL を返すなどがありますので
注意が必要です。

theApp を直接見ている分には抱えるデータの操作が同期化されていれば問題はありません。

それと、MFC のオブジェクトには、排他制御が自動で働くようなものは存在しませんのでこれは
自前で処理する必要があります。
なので、CObArray については別途排他制御が必要ということになります。
こちらはすべてのクラスオブジェクトについて同じことが言えます。

もちろん、スレッドが動いている間は参照しないなどがあれば特に必要はありません。


で、本題のリストビューコントロールへの操作ですが、こちらは最終的にSendMessage/PostMessage が
呼ばれている限り基本的には問題ありません。

これは、SendMessage/PostMessage が送り先のウィンドウの所属するスレッドの
コンテキスト上でメッセージを処理するように作られているためです。
これをメッセージ同期と呼ぶのですが、すでに昔話の世界に近くなってますw

という話をずいぶん前にMFC-MLでやった事がある。いまではアーカイブもないので当時のメンバーが
自分で保持しているメール以外には残っていませんがwww

引用返信 編集キー/
■3798 / inTopicNo.3)  Re[2]: MFC マルチスレッドについて
□投稿者/ Jitta (332回)-(2007/05/23(Wed) 22:28:40)
Jitta さんの Web サイト
No3794 (とっちゃん さん) に返信

なるほど。ありがとうございます。

 ListView の方は、動きを見ていて「大丈夫・・・なんだろう」と思ったのですが、CObjArray がまずいと思いました。
なので、Critical...ど忘れ でロックするようにしました。

 早く C# に移行したい。。。とか思っているのに、隣の席から C 言語(Programing Language C)のことで聞かれるし(縛)
(爆発じゃない、縛りだ)
引用返信 編集キー/
■3817 / inTopicNo.4)  Re[3]: MFC マルチスレッドについて
□投稿者/ とっちゃん (126回)-(2007/05/24(Thu) 13:01:28)
とっちゃん さんの Web サイト
No3798 (Jitta さん) に返信

>  ListView の方は、動きを見ていて「大丈夫・・・なんだろう」と思ったのですが、CObjArray がまずいと思いました。
> なので、Critical...ど忘れ でロックするようにしました。
>
CCriticalSection かな?軽量ロックと呼ばれるやつですが、OSによっては劇重w
どんなOSでも安定して軽いのは、EVENT ですね。MFC なら CEvent。APIなら、CreateEvent。

どっちも、オブジェクトを実体ごと渡すことで共有できます。というより共有しなきゃ話にならないww


>  早く C# に移行したい。。。とか思っているのに、隣の席から C 言語(Programing Language C)のことで聞かれるし(縛)
> (爆発じゃない、縛りだ)
怨念だwww

んー。。。もう、デクリメントできねーですよww
この間、ちょこちょこっとやってみようかとおもったんですが、1分で挫折しましたw

引用返信 編集キー/
■3819 / inTopicNo.5)  Re[1]: MFC マルチスレッドについて
□投稿者/ 渋木宏明(ひどり) (207回)-(2007/05/24(Thu) 16:54:51)
渋木宏明(ひどり) さんの Web サイト
> また、‘ダイアログの公開フィールドにある、CObjArray を操作しています’。
>  MFC の知識がほとんど無いので尋ねたいのですが、
> これらの(引用符で囲った)操作は、スレッド セーフでしょうか。

「そこだけ」ならスレッドセーフです。
それは CWnd がカプセル化しているウィンドウハンドルとは全く関係のない、「C++ のクラスメンバの操作」であるからです。

このレベルなら、.NET でも Control.Invoke は不要です。
「何故 Invoke が必要なのか」について、もう一度冷静に考えてみてください。

一方

> 通信スレッドでは、通信の結果を‘ダイアログにあるリスト ビューへ書き込んでいます’。

については、MFC でも失敗する可能性があります。
けど、動いてるんであれば正しい実装をしてるのかな?

MFC の CWnd はウィンドウハンドルをカプセル化していますが、MFC は CWnd のインスタンスとウィンドウハンドルの関連付けをスレッドローカルストレージ上のマップで管理しています。

なので、CWnd は CWne::Create() を実行したスレッド以外では、CWnd がカプセル化するウィンドウに対する操作は正常に機能しません。


引用返信 編集キー/
■3824 / inTopicNo.6)  Re[2]: MFC マルチスレッドについて
□投稿者/ とっちゃん (127回)-(2007/05/24(Thu) 20:33:16)
とっちゃん さんの Web サイト
No3819 (渋木宏明(ひどり) さん) に返信
>>また、‘ダイアログの公開フィールドにある、CObjArray を操作しています’。
>> MFC の知識がほとんど無いので尋ねたいのですが、
>>これらの(引用符で囲った)操作は、スレッド セーフでしょうか。
>
> 「そこだけ」ならスレッドセーフです。
> それは CWnd がカプセル化しているウィンドウハンドルとは全く関係のない、「C++ のクラスメンバの操作」であるからです。
>
MFC のクラスは、スレッドセーフにはなっていないので、こっちがアウトです。
こちらは、「Natiive C++ のクラスメンバへの操作だからこそ」です。

> 一方
>
>>通信スレッドでは、通信の結果を‘ダイアログにあるリスト ビューへ書き込んでいます’。
>
> については、MFC でも失敗する可能性があります。
> けど、動いてるんであれば正しい実装をしてるのかな?
>
こちらは、逆に実体渡ししてる限り失敗しません。
HWND はというか、Windows自身のメッセージキュー(MSMQではない)がスレッドセーフになっているので
SendMessage/PostMessage が呼ばれる部分についてはスレッドセーフになっています。


> MFC の CWnd はウィンドウハンドルをカプセル化していますが、MFC は CWnd のインスタンスとウィンドウハンドルの関連付けをスレッドローカルストレージ上のマップで管理しています。
>
ここは正しいのですが、


> なので、CWnd は CWne::Create() を実行したスレッド以外では、CWnd がカプセル化するウィンドウに対する操作は正常に機能しません。
>
こちらは、ちょっと違います。
CWnd を FromHandle/FromHandlePermanent で引っ張るときに、スレッドごとに管理されたマップリストで引き出すため、
異なるスレッドからの参照では、CWnd::Create() でつくられた場合でも、「関連付けされたMFCオブジェクトでは取得できない」となります。

MFC に限らず、Native C++ では、そのオブジェクトなりの実体を参照しているポインタなどがスレッドをまたぐとことで
参照先がことなるなどという事は絶対に起こりません。
なので、もともともっているオブジェクトを参照している限り、不整合が発生しうるのは、スレッド間でのメモリへの同時アクセスだけとなります(ローカル変数以外の変数へのアクセス)。

ただし、最初にも書いたように元からスレッドセーフではないオブジェクトを参照しますので、
その寿命がかならずオリジナルが長くなるようにスレッドの制御が行われるようになっている必要はあります。

まちがっても、スレッドより先にウィンドウが破棄されるなどが在ってはならないということですね。
このあたりが、Native 系のスレッド管理を繁雑にする要因の一つだったりしますw


引用返信 編集キー/
■3825 / inTopicNo.7)  Re[3]: MFC マルチスレッドについて
□投稿者/ 渋木宏明(ひどり) (210回)-(2007/05/24(Thu) 20:38:42)
渋木宏明(ひどり) さんの Web サイト
2007/05/24(Thu) 20:41:55 編集(投稿者)

> MFC のクラスは、スレッドセーフにはなっていないので、こっちがアウトです。

確かに、スレッド間で操作が競合する可能性があるなら排他制御は必須ですね。
でも、「引渡し先のスレッドでしか操作をしない」とか、操作が競合する可能性が無い時は排他はいらんでしょ?

> こちらは、逆に実体渡ししてる限り失敗しません。
> HWND はというか、Windows自身のメッセージキュー(MSMQではない)がスレッドセーフになっているので
> SendMessage/PostMessage が呼ばれる部分についてはスレッドセーフになっています。

いやだから、HWND 渡してるんじゃなくて CWnd* を渡してるんじゃないの?
だとすれば、こっちもアウトじゃない?

↓もそういう意味で書いてます。

>>なので、CWnd は CWne::Create() を実行したスレッド以外では、CWnd がカプセル化するウィンドウに対する操作は正常に機能しません。

引用返信 編集キー/
■3828 / inTopicNo.8)  Re[4]: MFC マルチスレッドについて
□投稿者/ とっちゃん (128回)-(2007/05/24(Thu) 21:06:47)
とっちゃん さんの Web サイト
No3825 (渋木宏明(ひどり) さん) に返信
> 2007/05/24(Thu) 20:41:55 編集(投稿者)
>
>>MFC のクラスは、スレッドセーフにはなっていないので、こっちがアウトです。
>
> 確かに、スレッド間で操作が競合する可能性があるなら排他制御は必須ですね。
> でも、「引渡し先のスレッドでしか操作をしない」とか、操作が競合する可能性が無い時は排他はいらんでしょ?
>
コレクションクラスなら、サブスレッドではセットオンリー、メインスレッドはゲットオンリーには成りませんかね?
そういう部分がない(スレッド稼働中に参照しない)なら、とくに制御は必要ありませんね。


>>こちらは、逆に実体渡ししてる限り失敗しません。
>>HWND はというか、Windows自身のメッセージキュー(MSMQではない)がスレッドセーフになっているので
>>SendMessage/PostMessage が呼ばれる部分についてはスレッドセーフになっています。
>
> いやだから、HWND 渡してるんじゃなくて CWnd* を渡してるんじゃないの?
> だとすれば、こっちもアウトじゃない?
>
こっちは、寿命が先に尽きると言うことは、可能性としてかなり低いと思います。
ないとは言い切れませんが、リストビューにセットしているということをかんがえれば、
スレッド稼働中にウィンドウが無くなってしまうという可能性は、バグしかないと思いますしw



完全に視点のズレのようです。

MFC が内部的に自動でスレッド間のアクセス制御を行ってくれることはない。

という所だけは一致していますけどw

引用返信 編集キー/
■3832 / inTopicNo.9)  Re[5]: MFC マルチスレッドについて
□投稿者/ 渋木宏明(ひどり) (211回)-(2007/05/24(Thu) 22:15:32)
渋木宏明(ひどり) さんの Web サイト
> >>こちらは、逆に実体渡ししてる限り失敗しません。
> >>HWND はというか、Windows自身のメッセージキュー(MSMQではない)がスレッドセーフになっているので
> >>SendMessage/PostMessage が呼ばれる部分についてはスレッドセーフになっています。
>>
>>いやだから、HWND 渡してるんじゃなくて CWnd* を渡してるんじゃないの?
>>だとすれば、こっちもアウトじゃない?
>>
> こっちは、寿命が先に尽きると言うことは、可能性としてかなり低いと思います。
> ないとは言い切れませんが、リストビューにセットしているということをかんがえれば、
> スレッド稼働中にウィンドウが無くなってしまうという可能性は、バグしかないと思いますしw

最初の投稿に

>通信スレッドに CResizingDialog を継承したダイアログ クラスのオブジェクトを引き渡しています

って書いてありますよね?

てことは、プライマリスレッドからワーカスレッドに渡しているのが HWND ではなく CWnd* じゃないの?つーことです。

その場合、CWnd のメンバメソッドを使用して CWnd がカプセルするウィンドウに対する操作を行ったら失敗するはずでしょ?(理由は既に述べたとおり)

プライマリスレッドからワーカスレッドに渡しているのが HWND で、ワーカスレッド内で CWnd::FromHandle() や CWnd::Attach(), CWnd::Detach() して得た CWnd 経由で、その CWnd がカプセルするウィンドウに対して操作を行う分には問題ありません。

引用返信 編集キー/
■3851 / inTopicNo.10)  Re[6]: MFC マルチスレッドについて
□投稿者/ とっちゃん (129回)-(2007/05/25(Fri) 13:58:28)
とっちゃん さんの Web サイト
No3832 (渋木宏明(ひどり) さん) に返信
>
> 最初の投稿に
>
> >通信スレッドに CResizingDialog を継承したダイアログ クラスのオブジェクトを引き渡しています
>
> って書いてありますよね?
>
> てことは、プライマリスレッドからワーカスレッドに渡しているのが HWND ではなく CWnd* じゃないの?つーことです。
>
多分、クラスインスタンスをポインタなりで渡してると思いますよ。
この部分は、おそらくスレッド側と受け渡しする情報がおおいからだと思います。


> その場合、CWnd のメンバメソッドを使用して CWnd がカプセルするウィンドウに対する操作を行ったら失敗するはずでしょ?(理由は既に述べたとおり)
>
いえ。ここは失敗しません。CWnd とて C++の単なるクラスインスタンスの一つです。
特殊な部分は、CWnd のオブジェクトではなく、それを逆引きするための、CWnd::FromHandlePermanent() にあります。
この逆引き(HWND から 関連付けされた CWnd インスタンスを引き出す)情報が、スレッドごとに管理されています。

なので、CWnd そのものが(派生クラスも含め)、この管理オブジェクトへのアクセスを起こす Attach/Detach の呼び出し(すなわちウィンドウの生成と破棄)を
行なわない限り、何の問題も発生しません。
もちろん、どこかで瞬間的に m_hWnd を書き換えるなど、C++ のインスタンスそのものへの操作はNGですが。

メッセージの送信は、m_hWnd の参照だけである限り、同期制御については不要です。
この部分は、C++ のインスタンスとしてそのメソッドがなにをおこなっているか?なので
すべてのインスタンスがデフォルトでスレッドセーフではない、C/C++ の世界では常にチェックが
必要な部分でもあります。

もし、そのあたりが怪しいというのがあるのであれば、スレッド側で、CListCtrl の m_hWnd を基に、スレッド側のエリア内で CListCtrl のインスタンス(ウィンドウではない)を起こし、そこに Attach すれば、解決します。もちろん、メインスレッド側の CListCtrl から Detach してはいけません。

ウィンドウメッセージをSendMessage/PostMessage(あるいは類似のメッセージ送信API)で投げた場合、その受信は
必ずウィンドウを作成したスレッド上で同期的に行われるようになっています(メッセージ同期機構)。

そのため、いつ誰がどこでどんなメッセージを送信したかにかかわらず、受信は常に1スレッドとなります。
この仕組みこそが、STA であり、COM の Apartment を支える仕組みです(多分、Invoke もこれで実装じゃないかとw)。

なので、メッセージを送る際に、「オブジェクトの状態に依存するもの」以外は、スレッド間で競合が発生することはありません。
通常のメッセージ送信(CListCtrl::InsertItemなど)では、CWnd のインスタンスに依存する情報は、HWND だけですので
これさえ、変わらないことが保障(=Detachしないということ)できていれば、スレッドセーフであると言い切ることができます。

むしろ、危険なのは、CListCtrl が自身の描画などでCObArrayなど別のオブジェクトに依存している場合です。

これがあるとする場合、InsertItem にかかわる一連の処理がシリアル化されている必要が発生します。
これは、再描画のタイミングと挿入処理が同時発生しないことを保証する必要があることを意味しており、
それを行うためには、どういう手段をとるにせよ、挿入の一連の処理をメインスレッド側で行わなければならない
ことを意味しています。

.NET であれば、Invoke() で処理できますが、Native C/C++ にはそのような便利な仕組みはないので、
SendMessage を使って処理することになります。
ここで、安易に Kernel系の同期オブジェクトなどを使うと、デッドロックします。
なので、必ず、メッセージを使って同期させる必要があります。

引用返信 編集キー/
■3853 / inTopicNo.11)  Re[7]: MFC マルチスレッドについて
□投稿者/ 渋木宏明(ひどり) (214回)-(2007/05/25(Fri) 14:08:10)
渋木宏明(ひどり) さんの Web サイト
>>てことは、プライマリスレッドからワーカスレッドに渡しているのが HWND ではなく CWnd* じゃないの?つーことです。
>>
> 多分、クラスインスタンスをポインタなりで渡してると思いますよ。
> この部分は、おそらくスレッド側と受け渡しする情報がおおいからだと思います。

ですよね。

>>その場合、CWnd のメンバメソッドを使用して CWnd がカプセルするウィンドウに対する操作を行ったら失敗するはずでしょ?(理由は既に述べたとおり)
>>
> いえ。ここは失敗しません。CWnd とて C++の単なるクラスインスタンスの一つです。

えー、失敗すると思ったけどなぁ。

http://homepage2.nifty.com/DSS/VCPP/MFC/Thread/ThreadMFC.htm

なんかにも、僕が言ってるのと同じことが書かれますが。。。

> ウィンドウメッセージをSendMessage/PostMessage(あるいは類似のメッセージ送信API)で投げた場合、その受信は
> 必ずウィンドウを作成したスレッド上で同期的に行われるようになっています(メッセージ同期機構)。

Windows の根っこはそうだけど、CWnd は自分がカプセルしているウィンドウを操作する時は SendMessage(), PostMessage() を使わないケースがあったはずです。


引用返信 編集キー/
■3855 / inTopicNo.12)  Re[8]: MFC マルチスレッドについて
□投稿者/ とっちゃん (130回)-(2007/05/25(Fri) 14:35:11)
とっちゃん さんの Web サイト
No3853 (渋木宏明(ひどり) さん) に返信
>>いえ。ここは失敗しません。CWnd とて C++の単なるクラスインスタンスの一つです。
>
> えー、失敗すると思ったけどなぁ。
>
> http://homepage2.nifty.com/DSS/VCPP/MFC/Thread/ThreadMFC.htm
>
> なんかにも、僕が言ってるのと同じことが書かれますが。。。
>
>>ウィンドウメッセージをSendMessage/PostMessage(あるいは類似のメッセージ送信API)で投げた場合、その受信は
>>必ずウィンドウを作成したスレッド上で同期的に行われるようになっています(メッセージ同期機構)。
>
> Windows の根っこはそうだけど、CWnd は自分がカプセルしているウィンドウを操作する時は SendMessage(), PostMessage() を使わないケースがあったはずです。
>
>
これは、CView と、CFrameWnd(CMDIChildWndを除く)。それと、CControlBar ですね。滅多にありませんが、これらだけは注意が必要。
もっとも、CControlBar については、別スレッドでまともに使えるような代物ではありませんがw
リンク先でも CView って言ってますよね。

それ以外は普通に HWND を抱えるオブジェクトです。
なので、ダイアログは単なるコンテナであると見なせた今回はその部分への言及はしてません。

実際は、CView や CFrameWnd もカプセル化が影響するのはDestroy/Create 以外では、他のオブジェクトにアクセスするところだけですので
アサート(多分、ASSERT_VALIDマクロ)されたところは、メッセージ送信では「ない」メソッド呼び出しだと思います。
どの道、アサートが出る=その呼び出しはやばいんじゃない?というのは、当たり前のことで、チップスに挙げるなら
なんでアサートが出るかと、どこで出してるかを書いて
それで、これこれはNGよ。にしないと...

Windows の Native 系マルチスレッドの世界は実に奥の深いジャングルですからねwww

引用返信 編集キー/
■3857 / inTopicNo.13)  Re[9]: MFC マルチスレッドについて
□投稿者/ 渋木宏明(ひどり) (215回)-(2007/05/25(Fri) 15:00:54)
渋木宏明(ひどり) さんの Web サイト
> なので、ダイアログは単なるコンテナであると見なせた今回はその部分への言及はしてません。

ダイアログ「なら」大丈夫とか、そういう区別は面倒くさいねー(今更ながら)
実際にアサートが出るパターンがあるので、別スレッドから渡された CWnd 派生クラス経由での操作は全回避してました>自分

引用返信 編集キー/
■3859 / inTopicNo.14)  Re[10]: MFC マルチスレッドについて
□投稿者/ とっちゃん (131回)-(2007/05/25(Fri) 15:51:06)
とっちゃん さんの Web サイト
No3857 (渋木宏明(ひどり) さん) に返信
> ダイアログ「なら」大丈夫とか、そういう区別は面倒くさいねー(今更ながら)

です。特にMFCがというわけじゃないにしても、MFC はあちこちにアサーションかましてるので
間違ってアサートされることがあるから余計ですw

まぁ、しょうがないところはあるんですけどね。C++ のインスタンスというオブジェクトと
HWND(に限らないけど、Windowsのハンドル型)のオブジェクトでは、べつべつのオブジェクト指向理論で実装されてますからね。

どうしたって、設計思想の違いから実装としてことなってしまう部分は出てきてしまいます。

MFC だからでもないし、C++ がダメという事でもないんですがねw

Native 系でやるかぎりどうしても避けられない問題です。だからマルチスレッドなアプリが増えなかった訳でもあるのですがw



> 実際にアサートが出るパターンがあるので、別スレッドから渡された CWnd 派生クラス経由での操作は全回避してました>自分
>
スレッドでアサーションされるってことは、ASSERT_VALID() してるところじゃないですか?
SendMessage() を呼んでるところでは今はないはずですが、まだ間違って設定してるところは残ってるかも...w

ダイアログは、スレッド側から、OnOK とか直呼びしなければ、大丈夫だったはずです。
まぁ、メッセージハンドラとそれ用の仮想関数とできちんと切り分けできてる人が造ったかどうかという
大きな問題はありますがw

引用返信 編集キー/
■3860 / inTopicNo.15)  Re[1]: MFC マルチスレッドについて
□投稿者/ シャノン (165回)-(2007/05/25(Fri) 16:10:00)
タイムリーな話題なのでポスト
http://forums.belution.com/ja/vc/000/403/62s.shtml
引用返信 編集キー/
■3867 / inTopicNo.16)  Re[2]: MFC マルチスレッドについて
□投稿者/ Jitta (340回)-(2007/05/26(Sat) 00:17:02)
Jitta さんの Web サイト
すみません。昨日、投稿する時間を作れませんでした。
具体的に抜き出してみました。もちろん、適当に編集しています。

class ダイアログ : public CResizingDialog
{
public:
    CObArray    結果リスト;
    CWinThread* 通信スレッドハンドル[256];
    int         通信中スレッド数;

    //{{AFX_DATA(ダイアログ)
    CListCtrl   通信状況リスト;
    //}}AFX_DATA
}

void ダイアログ::ステータス設定(int インデックス, LPCTSTR ステータス)
{
    // 処理中に通信状況をリアルタイム表示する
    LV_ITEM item;
    item.mask = LVIF_TEXT;
    item.iItem = インデックス;
    item.pszText = ステータス;

    通信状況リスト.SetItem(&item);
    通信状況リスト.Update(インデックス);
}

void ダイアログ::メッセージマップされた関数()
{
    // 通信スレッドを作って、
    int スレッド数 = 0;
    for (int index = 0; index < 通信状況リスト.GetCount(); index++) {
        if (通信相手リスト[index].通信するか == TRUE) {
            通信スレッドハンドル[スレッド数++] = 通信スレッド開始処理(this, index);
        }
    }
    // 終わるのを待ち、
    while (通信中スレッド数 != 0) {
        Sleep(1000);
        メッセージ処理(); // Peek して Translate して Dispatch
    }
    // 結果を表示する。
    通信結果を別のウインドウに引き渡す処理();
    CResizingDialog::OnOK();
}

void ダイアログ::通信スレッド開始処理(ダイアログ* 元ウインドウ, int index)
{
    通信スレッドClass *戻り値; // クラスの詳細省略
    戻り値 = AfxBeginThread(RUNTIME_CLASS(通信スレッドClass), ...);
    戻り値->ウインドウ = 元ウインドウ;
    戻り値->インデックス = index;
    通信中スレッド数++;
    return 戻り値;
}
〜〜〜〜〜〜〜〜〜〜

class 通信スレッド : public CWinThread
{
public:
    ダイアログ*     ウインドウ;
    int             インデックス; // ListView 上のインデックス
    /* その他、通信のためのデータ */
}

int 通信スレッド::Run()
{
    /* UDP で送って、返信を待つ */
    /* タイムアウトなどの振り分け処理の後、 */

    // ★★これっていいのかヨ!!★★
    this->ウインドウ->ステータス設定(this->インデックス, ステータス);
    this->ウインドウ->結果リスト.Add(ステータス);
    this->ウインドウ->通信中スレッド数--;

    ::PostQuitMessage(0);
    return CWinThread::Run(); // これもいるのかョ??
}
〜〜〜〜〜〜〜〜〜〜

私が危ないと思うのは、class 通信スレッド での、
    this->ウインドウ->ステータス設定(this->インデックス, ステータス);
    this->ウインドウ->結果リスト.Add(ステータス);
    this->ウインドウ->通信中スレッド数--;
の、3行です。
とりあえず、結果リスト、通信中スレッド数については public 公開をやめ、
操作のためのメソッドを追加し、CCriticalSection と CSingleLock でロックしました。
シャノンさんが引いてくださっているところを見ると、ステータス設定() でも、何らかの対策が必要?

ああ、もう。泣きたい。

引用返信 編集キー/
■3874 / inTopicNo.17)  Re[3]: MFC マルチスレッドについて
□投稿者/ とっちゃん (132回)-(2007/05/26(Sat) 12:15:18)
とっちゃん さんの Web サイト
No3867 (Jitta さん) に返信

> 私が危ないと思うのは、class 通信スレッド での、
> this->ウインドウ->ステータス設定(this->インデックス, ステータス);
> this->ウインドウ->結果リスト.Add(ステータス);
> this->ウインドウ->通信中スレッド数--;
> の、3行です。

ここの部分より「もっと」あぶないところが
通信スレッド開始処理()の中ですね。

戻り値 = AfxBeginThread(RUNTIME_CLASS(通信スレッドClass), ...);
戻り値->ウインドウ = 元ウインドウ;
戻り値->インデックス = index;
通信中スレッド数++; /// ここ!

多分「ここ!」の部分よりあとになってからスレッドがスタートしないとだめですよね?
なので、AfxBeginThread() の dwCreateFlags(うしろから2番目の引数)に
CREATE_SUSPENDED
をつけて作成し(これをつけることで、オブジェクトは作られるがスレッドはスタートしない状態となる)
ここ!のつぎの行で
戻り値->ResumeThread();
を入れることで安全にスレッドオブジェクトを初期化できます。


> とりあえず、結果リスト、通信中スレッド数については public 公開をやめ、
> 操作のためのメソッドを追加し、CCriticalSection と CSingleLock でロックしました。

結果リストは、スレッドから一方的に...って同時に複数のスレッドが立ち上がるから
ロックしておかないとだめですね。

カウンタの通信スレッド数は、InterlockedIncrement/Decrement で操作した方がいいですよ。
つねに両方1セットという場合は別ですが。
それと、通信スレッド数 は宣言に mutable をつけて、レジスタ操作されないようにしてください。
今のままだと多分最適化で // 終わるのを待ち、
while (通信中スレッド数 != 0)...
の部分でレジスタつかわれてしまう可能性が高いです。<原因不明のバグになります
たしか、Interloced 系は読み取り関数がなかったと思うので...w

> シャノンさんが引いてくださっているところを見ると、ステータス設定() でも、何らかの対策が必要?
こちらは、SetItem と Update メソッドの中身をみてください。
{_ASSERT( ::IsWindow( m_hWnd ) ); return ::SendMessage( m_hWnd, ... ); }
だと思いますけどw
これなら、問題はありません。段取りとして CListCtrl が居なくなることもないし、m_hWnd が変わることもありませんので。

スレッドの終了待機のところもうーん...な感じですが、こっちはうごいてるならまぁOKということでw

スレッドのRun で親を呼ぶ必要はないです。まぁあんまり...という感じはしますけどw
#全てバークレーならウィンドウメッセージいらないんだっけ?

というか、そういう形にしてるなら、ExitInstance を作ってそこで
// ★★これっていいのかヨ!!★★
をやった方がいい気がするw

引用返信 編集キー/
■3879 / inTopicNo.18)  Re[4]: MFC マルチスレッドについて
□投稿者/ 渋木宏明(ひどり) (217回)-(2007/05/26(Sat) 15:55:03)
渋木宏明(ひどり) さんの Web サイト
> それと、通信スレッド数 は宣言に mutable をつけて、レジスタ操作されないようにしてください。

volatile じゃん?

引用返信 編集キー/
■3881 / inTopicNo.19)  Re[5]: MFC マルチスレッドについて
□投稿者/ とっちゃん (133回)-(2007/05/26(Sat) 16:13:46)
とっちゃん さんの Web サイト
No3879 (渋木宏明(ひどり) さん) に返信
>>それと、通信スレッド数 は宣言に mutable をつけて、レジスタ操作されないようにしてください。
>
> volatile じゃん?
>
あれ?そうだっけ?
volatile は、const じゃなかったっけ?

逆だ...最近ダメダメだなぁ...
volatile つけて、最適化でレジスタ使わないようになるだ。
mutable が const メソッドで書き換え可能のフラグだ。

ちっとも覚えないなぁ...orz
#ちょっと前に話題になってたのに〜
引用返信 編集キー/
■3883 / inTopicNo.20)  Re[3]: MFC マルチスレッドについて
 
□投稿者/ 渋木宏明(ひどり) (218回)-(2007/05/26(Sat) 18:02:40)
渋木宏明(ひどり) さんの Web サイト
> void ダイアログ::メッセージマップされた関数()

これも危ういなぁ。

> // 終わるのを待ち、
> while (通信中スレッド数 != 0) {
> Sleep(1000);
> メッセージ処理(); // Peek して Translate して Dispatch
> }

みたいに、(僕の大嫌いな)DoEvents() してますよね。

てことは、スレッド待ち合わせの間に何度でも「メッセージマップされた関数」に入ってくる可能性がありませんか?

ぱっと見、再入したらわやくちゃになりそーなコードに見えるけど、大丈夫なのかな?

> ああ、もう。泣きたい。

「ナニがしたいのか」を把握して、書き直しちゃった方が早いんじゃないすか?

引用返信 編集キー/

次の20件>
トピック内ページ移動 / << 0 | 1 >>

管理者用

- Child Tree -