[過去ログ]
マルチスレッドプログラミング相談室 その4 (1001レス)
マルチスレッドプログラミング相談室 その4 http://pc8.5ch.net/test/read.cgi/tech/1130984585/
上
下
前次
1-
新
通常表示
512バイト分割
レス栞
抽出解除
レス栞
このスレッドは過去ログ倉庫に格納されています。
次スレ検索
歴削→次スレ
栞削→次スレ
過去ログメニュー
225: デフォルトの名無しさん [sage] 2006/01/27(金) 17:33:55 A.EXE と B.DLL があり、B.DLL が foo() という関数をエクスポートしており、 foo() は内部的に strtok 等の C ランタイム関数を呼ぶとする。 B.DLL 内部で _beginthread(ex) したスレッドの中で foo() を呼ぶのは当然問題 ないはずだけど、A.EXE が内部で CreateThread または _beginthread(ex) した スレッドの中で B.DLL の foo() 関数を呼び出した場合、メモリリークするの? B.DLL が DLL_THREAD_ATTACH/DETACH できちんとスレッドごとの初期化・後始末 をしていれば大丈夫そうな気がするんだけど、VC とか BC とかの C ランタイム ライブラリはこの辺どうなってるんだろう。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/225
227: デフォルトの名無しさん [sage] 2006/01/27(金) 20:30:26 >>225 むしろ逆。 B.DLL内部で_beginthread(ex)したスレッドからA.EXE中の関数bar()を呼ぶとリークする。 ただしA.EXEがCRTとスタティックリンクしていて実行環境がWindows Server 2003より前の場合。 ttp://codezine.jp/a/article.aspx?aid=235&p=2#col4 CRTはソース公開されてるから気になるなら読むといいよ。 Express Editionだと付いてるか知らんけど。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/227
228: 225 [sage] 2006/01/27(金) 23:32:29 >>227 > B.DLL内部で_beginthread(ex)したスレッドからA.EXE中の関数bar()を呼ぶとリークする。 コールバック関数とかも、そのコールバック関数が自EXE で _beginthread(ex) したスレッドから呼ばれないなら C ランタイムを使えないことになりますね。 >>227 の質問を繰り返すことになりますが、A.EXE 内部で _beginthread(ex) した スレッドから B.DLL 中の関数 foo() を呼ぶのは大丈夫なんでしょうか。 DllMain の DLL_THREAD_ATTACH/DETACH はこのへんの問題を解決するための 機構だと思ってるので、きっと大丈夫なんだと自分に言い聞かせるようにし てるんですが…。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/228
229: 225 [sage] 2006/01/27(金) 23:34:18 >>227 > CRTはソース公開されてるから気になるなら読むといいよ。 VC6 のソースを見る限りでは、DLL_THREAD_ATTACH/DETACH でスレッドごとの 初期化/後始末をしているようです。 CRT を使う DLL に関して、VC6 のソースを見た感じでは、その DLL が C ランタイムをスタティックリンクしている場合は _DllMainCRTStartup 内で DllMain を呼ぶ前に DisableThreadLibraryCalls を呼ばないようです。 MSVCRT.DLL を使用する場合は、ユーザー定義の DllMain がない場合のみ、 DisableThreadLibraryCalls を呼んでいるようです。DllMain があった場合、 DllMain 内部で DLL_THREAD_ATTACH/DETACH が必要になる可能性があるから DisableThreadLibraryCalls を呼ばないようにしてるのでしょう。おそらく。 だから、EXE 側で作成したスレッド内で、DLL 側の C ランタイム関数を使う 関数を呼び出しても、DLL_THREAD_DETACH で後始末が行われるのでメモリリーク は発生しないのではないかと思ってますが、それが明記されているドキュメント が見当たらないので気になっています。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/229
230: 225 [sage] 2006/01/27(金) 23:36:13 EXE 側で作成したスレッド内で、DLL 側の関数(内部的に C ランタイム関数を 使うもの)を使えない(使うとメモリリークする)としたら、まともなマルチ スレッドアプリケーションなんて開発出来ない気もします。 せめて malloc/free/new/delete くらいは問題なく使えるようでないと…。 でも、 http://support.microsoft.com/default.aspx?scid=kb;ja;104641 を見ると malloc も駄目みたいですよね…。実際のところどうなんでしょ。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/230
234: 225 [sage] 2006/01/28(土) 14:56:16 ちょっと試してみました。 B.DLL void foo(void) { char dummy[256]; strtok(dummy, ""); } A.EXE DWORD WINAPI ThreadProc(void *pvParam) { foo();//B.DLL 内の関数 return 0; } void test(void) { HANDLE hThread; DWORD dwThreadId; int i; for(i = 0; i < 10000; i++){ hThread = (HANDLE)CreateThread(NULL, 0, ThreadProc, 0, 0, &dwThreadId); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } } http://pc8.5ch.net/test/read.cgi/tech/1130984585/234
235: 225 [sage] 2006/01/28(土) 14:57:27 >>234 の続き 1.A.EXE: BCB5 で作成、B.DLL: VC6 libcmt.lib で作成 2.A.EXE: BCB5 で作成、B.DLL: VC6 msvcrt.lib で作成 3.A.EXE: BCB5 で作成、B.DLL: BCB5 で作成 結果: 1:DllMain で DisableThreadLibraryCalls を呼ぶとメモリリークする (test を呼ぶ度にメモリ使用量が増える) DllMain で DisableThreadLibraryCalls を呼ばなければメモリリークしない (test を何回呼んでもメモリ使用量は変わらない) 2:DllMain で DisableThreadLibraryCalls を呼んでも呼ばなくてもメモリリークしない 3:DllMain で DisableThreadLibraryCalls を呼んでも呼ばなくてもメモリリークする ちなみに void foo(void) { char* dummy = (char*)malloc(128);// char *dummy = new char[128]; free(dummy);// delete[] dummy; } の場合、どの場合においてもメモリリークしない http://pc8.5ch.net/test/read.cgi/tech/1130984585/235
236: 225 [sage] 2006/01/28(土) 14:59:56 >>235 の続き 結論: BCB5/VC6 で作成した B.DLL においては、C ランタイムルーチンのうち、少なくとも malloc/free/new/delete しか使わない分には、A.EXE で作成したスレッド内で B.DLL の関数を呼び出してもメモリリークしない。スレッドの作成に _beginthread(ex) を 使う必要もない。 VC6 で MSVCRT.DLL とリンクする B.DLL では、A.EXE で作成したスレッド内で B.DLL の関数(内部で C ランタイム関数を呼ぶもの)を呼び出してもメモリリークしない。 VC6 で libcmt.lib とスタティックリンクする B.DLL では、DllMain で DisableThreadLibraryCalls() を呼ばなければ、A.EXE で作成したスレッド内で B.DLL の関数(内部で C ランタイム関数を呼ぶもの)を呼び出してもメモリリーク しないが、DisableThreadLibraryCalls()を呼ぶと、B.DLL が内部で呼び出している C ランタイムルーチンによってはメモリリークする可能性がある。 他にも色んな組み合わせが考えられますが、とりあえず自分が知りたかった 「A.EXE で作成したスレッド内で B.DLL 内の関数を呼び出してもメモリリーク しないのか」は検証出来ました。この程度の実験で検証出来たと断言して良い のかはわかりませんが。 BCB5 の C ランタイムルーチンは DLL_THREAD_ATTACH/DETACH をきちんと処理して いないものと思われます。BCB5 で作った DLL はマルチスレッドアプリケーション からは使いにくいですね。 VC7 以降とか GCC とかではどうなんでしょうね。 検証の仕方が間違ってたらご指摘下さい。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/236
238: 225 [sage] 2006/01/28(土) 18:02:37 > 複数のスレッドで同時に1つのワークエリアを参照していれば、 > 各スレッドがお互いに1つのワークエリアをぶっ壊しあう。 マルチスレッド対応のランタイムでは、そのような問題は起こらないことが 保証されているものだと思ってました。 _beginthread(ex) の説明を読む限り、メモリリーク問題以外については 何も記述されていません。 > つか、なんでCreateThread()で試してるんだ? 確認したかったのは、「A.EXE で作成したスレッド内で B.DLL 内の関数を 呼ぶことが出来るかどうか」だからです。 A.EXE は BCB5 で作成していて、B.DLL は VC6 で作成しています。 B.DLL 内の C ランタイムルーチンを使うのに、A.EXE の _beginthread(ex) を使っても無意味なのは初めからわかりきっています。 また、>>235 では書きませんでしたが、3.の場合、C ランタイムルーチン はスタティックリンクされてるので、やはり A.EXE の _beginthread(ex) と B.DLL の _beginthread(ex) は全く別物です。 > 試すまでもなく駄目なの分かり切ってるだろ。 メモリリークという点だけ見れば、駄目とは言い切れないという結果に なったと思うのですが。元々 DLL_THREAD_ATTACH/DETACH で解決し得る 問題だと思ってるので、この結果がおかしいとは思えません。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/238
240: 225 [sage] 2006/01/28(土) 20:18:06 >>239 > なぜなら、Cランタイムをスレッドセーフにする肝心の処理を_beginthread*()がやるから。 _beginthread(ex) と _endthead がやるのは、スレッドごとに必要なメモリ の割り当てと解放を行うことですよね? _beginthread を使わなかったせいで、ランタイムルーチンを呼び出し時に そのスレッドではまだメモリが割り当てられてなかったなら、その場で動的に 割り当てれば問題ないでしょう。実際、動的に割り当ててると思うんだけど。 問題なのは、スレッドの終了を知ることが出来なくなるせいで、割り当てた メモリを解放する機会が得られなくなり、その結果としてメモリリークが発 生する、ということではないの? _beginthread(ex) を使わないことによって、排他処理的な問題が起こるとは どこにも書いてないですよね?起こらないとも書いてないけど。 排他処理的な問題が起こると明記されてるのは、シングルスレッド版の ランタイムを使った場合の方だけです。 マルチスレッド版の方では、メモリリークのことしか書いてないと思う んですけど。 > ついでに言えば、問題はB.DLLだけの話ではなくて、A.EXEのランタイムに > 何を使ってるか? というのも重要。にも関わらず、>>235でその点に > 一切触れていない点からも、喪前さんの理解が足りないことが伺える。 A.EXE は BCB5 で作成、B.DLL は VC6 で作成と書いてます。 理解が足りないのは認めますが。 長文ばかりで申し訳ない。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/240
242: 225 [sage] 2006/01/28(土) 21:09:14 >>241 真面目に質問してるつもりなんですが、そういうのが伝わらない あたりが 2ch のイヤなところですね。 どこかにこの件に関してきちんと解説したウェブサイトなり書籍 があればご紹介頂きたかったですがね。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/242
244: 225 [sage] 2006/01/28(土) 21:40:13 >>243 やっぱりそうですか。(^^; >>235 の結果から、(この検証が正しければ)メモリリークに関しては 処理系依存ということになったわけだし、ドキュメント化されてるわけ でもないようだから、結局のところ結論なんて出ないんだろうな…。 「A.EXE で作成したスレッドから B.DLL の関数を呼び出すとメモリリークする」 のだとすると、Susie プラグインとか、自分ではメンテナンス出来ないプラグイン 方式の DLL を使うアプリをマルチスレッド化するのは非現実的ということになっ てしまうのか…。ほとんどの DLL は C ランタイムを使用してるだろうから。 一度作成したスレッドはアプリが終了するまで使い回すようにするしかないの だろうか…。なんか納得行かないな。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/244
249: 225 [sage] 2006/01/29(日) 04:20:43 >>248 > 「EXE・DLLの両方で同じライブラリ使ってれば、相互に呼び合いしても問題ない」 DLL は他人が作ってるので自分でいじれないし、EXE は GUI の 関係上 BCB しか使えないので。 > 「泥沼に足突っ込みたくなければ推奨されてるやり方を使え」 EXE で作成したスレッドから DLL の関数を呼ぶ場合の推奨されてる やり方とは?EXE/DLL ともに MSVCRT を使うことですか? > AdvancedWindowsなりMSDNなりCRTのソースなり自分で読んで勉強してくれ MSDN と CRT のソースは自分なりに熟読してみたつもりです。 CRT のソースから、>>235 の結果は予想通りでした。 でも、3.の結果は予想通りだけど納得行かない。 BCB5 の C ランタイムライブラリのバグと言うべきなんじゃないだろうか。 仕様で片付けていい問題ではないような…。 AdvancedWindows を読んでもう少し勉強してみます。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/249
267: 225 [sage] 2006/01/30(月) 22:43:46 _beginthread(ex) の説明のところに、CreateThread だとメモリリークして しまう C ランタイム関数の一覧でも書いてあれば良いんですけどね。 ttp://support.microsoft.com/?scid=kb%3Ben-us%3B104641&x=11&y=5 には malloc とか fopen でもメモリリークするようなことが書かれてるけど、 実際に試してみると、VC6/BCB5 どちらの場合もメモリリークしてるようには 思えないです。 ここに書かれてないものでは、rand() を使うとメモリリークしますね。 今回の呼び出しの結果が前回の呼び出しに依存するようなタイプの関数は 全滅なんでしょうね。 http://pc8.5ch.net/test/read.cgi/tech/1130984585/267
メモ帳
(0/65535文字)
上
下
前次
1-
新
書
関
写
板
覧
索
設
栞
歴
スレ情報
赤レス抽出
画像レス抽出
歴の未読スレ
AAサムネイル
Google検索
Wikipedia
ぬこの手
ぬこTOP
0.050s