- - PR -
C#のLock処理について
投票結果総投票数:8 | |||
---|---|---|---|
Windows | 8票 | 100.00% | |
|
投稿者 | 投稿内容 | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2006-03-23 19:19
private bool flagA = false;
private bool flagB = false; ここら辺の定義にvolatileつけてみたり。 --追記 やっぱり関係なさそうかな… [ メッセージ編集済み 編集者: なちゃ 編集日時 2006-03-23 19:55 ] | ||||||||||||||||||||
|
投稿日時: 2006-03-23 19:40
結局、flagAとflagBは順次「true」の設定され、排他制御がかかっているので、CallAnotherMethodが呼ばれなかったことから、どこかで、消えてしまったと推測したのですが、この辺り、別の可能性もございましたら、教えて頂きたいです。 | ||||||||||||||||||||
|
投稿日時: 2006-03-23 22:09
> // ProcessBには、UnManagedCodeの内容があり、
> // 終わりにGC.Collect()が必ず行われる ムダ。 GC.Collect は、マネージドメモリのガベージをコレクトします。マネージされていないものをコレクトするわけではありません。 (っつうか、マネージしているからコレクトできるわけで、マネージしていないものをどうやって知るのさ?) 例えば、エクセルを COM 連携で起動したとき、Quit しないとエクセルプロセスは残ります。ガベージコレクトを行っても、「プロセス終了」を行わないと、プロセスが終了しないというリークが発生することになります。
これもおそらくムダ。 StartProcessA が true を返さないと StartProcessB が呼び出されないなら、CallAnotherMethod は、少なくとも StartProcessA が true を返さないと実行されない。 だったら、こうでいい。
3,4,…8にどの様な処理が入るのかわからないけど、少なくとも0の処理が終わっていないと CallAnotherMethod は呼ばれないのだから、必要ないでしょ。 さらに、EventHandleClass は ParentClass の動的な変数なんだから、flagA, flagB が初期化されるのは1回だけで、もし eventID 3〜8で false にするんだとしても、StartProcessB が終了するまでロックしているはずだから、結局実行されないんじゃない? んで、少なくとも eventID 0,1,2は、同じスレッドからコールされますよね?だとすると、eventID 1が lock ステートメントを抜けるときに、ロックが解除されるんじゃないかなぁ? 別のスレッドからコールされるなら、0が終わるまで1,2はブロックされるわけで。そうすると、何時 CallAnotherMethod が呼ばれるんだろう? あ、eventID 1,2は別スレッドから呼ばれるのか。へぇ?どうやって?呼ぶはずのメソッドは、protected なのに。どう見ても、StartProcessA でデリゲートを設定しなきゃいけいないような気がするけど、それを、いつ、行うんだろう? eventID 0の case ブロックに break がないのは、ただの転記ミスなんだろうな。 だいたい、flagA, flagB は、ProcessA, ProcessB が終了したかどうかということを示しているわけですが、プロセスが終了するまでブロックするなら、なんのためにスレッドを分ける必要があるんだろう? で、もし、スレッドごとに ParentClass インスタンスを作るなら、EventHandleClass インスタンスもスレッドごとに作られるので、結局ロックされるスレッドはない、、、ということになってしまうわけで。 ホント、パズルですねぇ。。。 「転記しなかったコードで色々制御しているんだ」ということなら、公開するコードは現象が確認できる最低限のコードにしてください。書かれていないことをあれこれ想像するのは、本当に疲れますから。 [ メッセージ編集済み 編集者: Jitta 編集日時 2006-03-23 22:19 ] | ||||||||||||||||||||
|
投稿日時: 2006-03-23 23:13
どーっかで EventHandleClass のインスタンスをlockしてませんかね?
っておっしゃってますが、Cancel処理を呼び出すスレッドがロックを取得していたら 同じ動作になると思いますので、DeadLockみたいな状態になっている可能性も あるように思います。 ところで、GC.Collectを実行しない場合は正常に動くんですか? | ||||||||||||||||||||
|
投稿日時: 2006-03-24 02:33
どうも難しいですね.
同期,非同期という言葉の使いどころがおかしい様な気がします. 以降,揚げ足を取るというつもりではないので悪しからず.私の無知故です.
応答を待たないのであれば,非同期ではないでしょうか.
非同期なのか同期なのかよくわかりません.
見た感じロックしているのは一つのインスタンスなので「デッドロック」という状態は有り得ないでしょう.排他制御で用いるデッドロックという状態は「お互いがお互いの開放待ち」という状態です.今回の場合はあったとしても行列(先頭がどかない)待ちではないでしょうか.(他でロックしているのならばその限りではない) ちなみに
というのは,今ではかなり悪いロックのやり方であると言われています.これでは実際にロックしたい箇所がロックできない事が大いに有り得ますからね.詳細は調べてみてください. Layer-1 Layer-2 という表現がよくわからないのですが,何か特別な意味があるのでしょうか? 結局排他制御したいリソースは,flagA と flagB のみなのでしょうか? false になるのは最初だけで,以降 true にしかならないのならば,排他する必要はあるのでしょうか. 単純に,case 3〜8 が非常に長い処理を行っている,という事はないでしょうか? _________________ 囚人のジレンマな日々 | ||||||||||||||||||||
|
投稿日時: 2006-03-24 09:28
ご回答ありがとうございます。
ProcessBのライブラリにはManagedとUnmanagedコード両方存在するので、GC.Collect()の意図はメモリリークを防ぐためで、詳細は不明です。ただ、この中のGC.CollectはC#側に影響がないかと。。。
まず、ProcessAの結果は非同期でしか分からないので、ここのif(StartProcessA())はあくまで相手に通知できたことだけなので、相手は受け取ったかの判断にはなりません。 あと、ProcessAの結果を受けてから、ProcessBに進むやり方もあるが、ProcessAの結果を待ってる間に、ProcessBを先に行う経緯もあります。
ここで、0、1、2とも異なるスレッドからコールされるので、デバッガでみる限りいつもロックがかかってます。仰るとおり、非同期で呼ばれるprotectedメソッドは全て各クラスのオブジェクト生成時の初期化処理で行っています。
すみません。転記コードにミスがあります。EventHandleClassはParentClassの状態遷移を担う役目なので、関係は1対1です。 「歴史」のある構成で、改善すべき点が多いが、本当に申し訳ありません。 | ||||||||||||||||||||
|
投稿日時: 2006-03-24 09:59
ご指摘ありがとうございます。ここに自分も気になりますが、これから調べてみます。どなたは何かの関連情報をお持ちでしたら、ご教授お願い致します。 | ||||||||||||||||||||
|
投稿日時: 2006-03-24 21:36
まず訂正。
2006-03-23 22:09 に『eventID 1が lock ステートメントを抜けるときに、ロックが解除されるんじゃないかなぁ?』と書きましたが、これは誤りでした。Monitor.Enter を呼び出したのと同じ回数 Monitor.Exit を呼び出さなければ、ブロックは解除されません。 掲示板を見ている人は、あなたの会社にいて、あなたと同じプロジェクトを担当していて、あなたと同じソースコードを見ているわけではありません。また、あなたの事情などもわかりません。この点を、しっかりと記憶しておいてください。 その上で、「解決したい問題」と、問題を解決するために必要な「情報」を提示する必要があります。 今回問題になるのは、 1.GC.Collect によって回収されるもの 2.処理の実行順序 3.ロックの範囲 4.順次処理したい処理 だと思います。 コーディングの意図と、実際にコーディングされた結果にズレがあると、「バグ」として検出されます。検出されたバグを潰すためには、コーディングの意図、すなわち設計と、実際のコードにどの様な差異があるかを見つけなければなりません。 1.GC.collect によって回収されるもの
GC.Collect をしたからといって、メモリリークが防げるわけではありません。Collect を明示的に指示することによって回収可能なメモリは、明示的に指示しなくてもいずれ回収されます。つまり、明示的に呼び出す必要はありません。 GC.Collect をコールしても、メモリリークは発生し得ます。例えば、Win32API を使って、グローバルヒープからメモリ確保を行った場合、明示的に GlobalFree を実行しなければ、リークします。これは GC が管理するものではないため、GC.Collect では回収されません。 通信をするために、ポートを開くと思います。このポートもアンマネージドリソースです。明示的に閉じなければ、排他がかかったままになります。 もっとも、フレームワークで用意されているものは、ファイナライザで Dispose するように実装されていることが期待できますから、GC.collect でマネージドメモリがファイナライズされるときに閉じられるでしょう。 ただし、コレクトされるのは参照が切れているもののみですから、参照がつながっている場合はコレクトされません。それどころか、ジェネレーションが上がり、自動実行時に回収されなくなるかもしれません。 これらのことより、GC.Collect は「アンマネージコードを使っている部分のメモリリークを防ぎたい」という設計意図を満足しないため、「バグ」であると判断します。これは GC.Collect がバグなのではなく、GC.Collect でメモリリークがなくなるという設計思想がバグである、という意味です。 GC.Collect を実行すると、保持しているメモリから参照できないメモリが回収されます。
このとき、t は参照が切れているため、回収対象になると思います。someMethod の実行がどうなるのか、検証が必要です。 まず、このようなコードで、実行中であるにもかかわらず、回収されて消えてしまうのか検証することで、懸案事項のひとつは解消すると思います。 実は、Thread が IDisposable でないことが不思議です。スレッドハンドルをどこかで閉じてやる必要があると思うのですが。Finalize がオーバーライドされているから、その中でやっているのかな? 2.処理の実行順序 この情報は提示されていません。 意図通りに処理が実行されているのか、確認する必要があります。これは単純に、時間とスレッド ID、通過ポイントを出力して確認するということでいいでしょう。 3.ロックの範囲 この情報は、実際のコードのみ提供されています。設計と実際のコードが一致しているかどうか、検証してください。 また、ロックの範囲は「短く」「少なく」実装することが望ましいです。本当にロックしなければならないところだけがロックされているか、確認してください。この意味で、現在のコードは「長く」「多く(ソース上は一箇所だが、処理の流れを一本の線で表すと、多数になる)」実装されているため、見直しが必要ではないかと考えます。 4.順次処理したい処理 この情報は提示されていません。 ProcessA と ProcessB が終了していれば、AnotherMethod を実行するということはわかりました。しかし、ProcessB だけが実行されることもある、とのことです。この辺りのメソッドが実行されるユースケース、および条件が整理されていないように感じます。少なくとも、私は読み取れませんでした。 |