- - PR -
アンマネージドとマネージドのスレッド対応
投稿者 | 投稿内容 | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-06-26 17:03
とっちゃんさん、ありがとうございます。
OCXは他社提供のもので詳しくは中身をみていませんが しかし元々VB6用にVC6で作ったものを単純にVC2002にコンバートしたものなので特には何もしていないと思います。 自分もOCXを作ったことがないものでよく分からずに申し訳ないのですが、どこか(ソリューションの設定やコード等)を確認すれば分かるものでしょうか? またVB.NETのAP側でスレッドをコールするところを以下のようにしてみました(一部省略) 'スレッドコール For count = 1 To 100 Dim th As New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf Me.OCXCall)) th.ApartmentState = Threading.ApartmentState.STA 'STAを設定 th.Start() Next 'スレッドメソッド Private Sub OCXCall() Me.OCXObject.GetData(); End Sub 上記のようなコードでも結果は同じで、排他が効きませんでした。 (もちろんMTAでも同様に効きませんでした) そもそもVB.NETのAP(EXE)のスレッドモデルをSTAに設定というのはやり方が間違っているのでしょうか? 自分の知識不足で、教えて頂きたいことばかりで申し訳ありませんが宜しくお願いします。 | ||||||||||||||||||||
|
投稿日時: 2007-06-26 17:12
同一スレッドでメソッドが終わる前に同じメソッドが呼ばれる、 というのはメソッドで再帰が起こっている場合意外、 ありえません。 メソッドで再帰がおきているなら、ATLが 虫持ちということになりますね。 と、普通に考えたらひどりさんと同じ結論になったんですが、 もしかしてデータのロックとアクセス、アンロックは別メソッドですか? >このDLLのあるメソッドでATLサービスEXE側の@データ生成、Aデータ転送、Bデータクリアというを三つのメソッドをコールして完結するものがあります。 APからATLのデータ生成メソッドを呼ぶ APからATLのデータ転送メソッドを呼ぶ APからATLのデータクリアメソッドを呼ぶ これを順番に行っていて、 ATLのデータ生成メソッドでLock ATLのデータクリアメソッドでUnlock を行っているんではないでしょうか? だとすると、LockするスレッドとUnlockするスレッドが別になりますから、 納得いきます。 こうだったとするなら、 ATLのデザインが根本的に間違っています。 >このことに関して対応方法をご存知の方がいらっしゃれば教えていただけないでしょうか? ロックはメソッド内で簡潔すべきです。 ロックを、メソッド間で保持するというのは、 かなりの特殊な場合以外やりません。 排他的なリソースをメソッド間で共有する場合、 ハンドルという形でリソースに関する情報を渡すべきです。 リソースが一つしかないなら、ATL内部に変数を一つ用意するだけでもいいでしょう。 ATL側でリソースを使用しているかどうか、 スレッド間共有の変数を作成し、 その変数を同期機構でメソッド単位で保護します。 各メソッドでその変数に同期的にアクセスし、 リソースアクセスが行われているかを確認すれば 問題は回避できます。 >またこれらについて解説しているようなサイトや書籍があれば、あわせて教えて頂ければと思います。 マルチスレッドの基本ですし、 プログラムのデザインの基本だと思いますので、 他人のソースをたくさん読んだらいいでしょう。 [ メッセージ編集済み 編集者: れい 編集日時 2007-06-26 17:14 ] | ||||||||||||||||||||
|
投稿日時: 2007-06-26 18:22
れいさん、ありがとうございます。
自分も最初はそこを疑ったんですが、APもOCX、DLLもメソッド自体は単純で再帰するようなところがありせんでした。
ロック、アンロックは同一メソッドです。 DLL側だけで排他をかけており具体的には(だいぶ端折って書きますが) ※DLL内のGetDataメソッド GetData() { HANDLE hMutex = CreateMutex(NULL,FALSE,"GetData_LOCK_KEY_STRING"); WaitForSingleObject(hMutex,INFINITE); MakeData(); // これをATLのデータ生成メソッドだと思ってください TransData(); // これをATLのデータ転送メソッドだと思ってください DataClear(); // これをATLのデータクリアメソッドだと思ってください ReleaseMutex(hMutex); CloseHandle(hMutex); } というようなもので、このメソッドをマルチスレッドで呼んでも、先に走っているGetDataでReleaseMutexされない限り、後のGetDataではWaitForSingleObjectで待つと考えています。 しかしVB.NETのマルチスレッドで呼ぶと、先に走っているGetDataのReleaseMutexが呼ばれる以前に、後からのGetDataのWaitForSingelObejctがWAIT_OBJECT_0で抜けている状態です。(ログ等で確認しました) 実際にアンマネージドやマネージド拡張ではちゃんと待つのでコードとして問題ないと思っていましたが、どこかマズい部分があれば、申し分かりませんが教えて頂ければと思います。 宜しくお願いします。 | ||||||||||||||||||||
|
投稿日時: 2007-06-26 18:56
VB6のコードをコンバートしたりOCXつかって呼び出したりというのは とても試せませんが、 ATL呼び出してもCOM呼び出しても、 私のところでは、Mutexはちゃんと動いてます。 GetDataをマルチスレッドで2回呼び出した場合、 2回結果がもどっていますか? ログをとってみたとき、 同じスレッドIDで全てのメソッドが2回よばれていますか? 呼ばれているなら、その順番はどうなっていますか? エラー処理はどうしていますか? VBにエラーが戻ってきていませんか? | ||||||||||||||||||||
|
投稿日時: 2007-06-27 01:23
あんまりよく知らないのではずしてるかもしれませんが、
>AP ⇔ OCX ⇔ DLL ⇔ ATL ATL呼び出した時点で、OCXの方が再入可能な状態とかになってませんかね? もし再入可能だと、別スレッドからのOCX呼び出しが割り込んでしまって、 同一スレッドだから当然Mutexは取得済みで排他できなくなります。 --追記 というか、OCXがSTAならそもそもMutexでの排他自体に効果がないですね(1プロセス内なら)… OCXをMTAにできる?のならMutex等で普通に排他できると思いますが… [ メッセージ編集済み 編集者: なちゃ 編集日時 2007-06-27 01:39 ] | ||||||||||||||||||||
|
投稿日時: 2007-06-27 03:33
?! そうなんですか? 名前なしMutexならロック範囲がプロセス内なので STAで効果ないのもわかりますが、 名前有りMutexなのでシステムグローバルに効くと思うのですが。 | ||||||||||||||||||||
|
投稿日時: 2007-06-27 09:22
れいさん、なちゃさん、お返事りがとうございます。
みなさん、お忙しいところ再々アドバイスを頂き、感謝、感謝です。
はい、戻ってきます。
はい、同じスレッドIDです。(DLL側でGetCurrentThreadIDで取得した結果です) シーケンスとしては先発のGetDataメソッドの途中で後発のGetDataメソッドが走り始め、 その後発のメソッドのデータ転送処理中に先発のメソッドのデータクリア処理が動き、データ転送の途中でデータが空っぽになり、データ転送メソッドがエラーを返し、結果的に後発GetDataメソッド自体がエラーを返すということになってしまっています。
DLLのメソッドでは、実際は排他処理を含め、全て戻り値やtyr〜catch、その他でエラー判定を行っており、エラー値を呼び元のAPのほうに返しています。 現在は上記に書いたようにデータ転送で、データが空っぽのエラーを返してきています。
確実なことはいえませんが、今のOCXは多分STAなのかなと思います。 DLL側で(今回の件と関係ないですが)、ある別のATLのサービスを使っていますが、アパートの問題でMTAなら一度Openすればあとはそのままでよし、STAならば毎度Openするという使い分けの部分があり、OCX(VB)から呼ばれた場合は無条件にSTAでの扱いになっています。 もともとVB6用のOCXですから、VB6の場合は無条件でSTAでも問題なかったのかなと・・・ そういったことの確認方法や、もしOXCがSTAならMTAにする方法とかが調べているのですがまだ分かりません。 もしご存知なら教えていただければと思います。
今回の件と全く同じ現象かは分かりませんが似たような話題がありました http://forums.belution.com/ja/vc/000/404/25.shtml http://forums.belution.com/ja/vc/000/404/33.shtml といっても解決策ははっきりしてないようですが・・・ 勝手なお願いばかりです申し訳ありませんが宜しくお願いします。 | ||||||||||||||||||||
|
投稿日時: 2007-06-27 11:55
Mutexがシステムグローバルかとかそういうことではなくて、
OCXがSTAなら、このメソッドに入ってきた時点で必ず同一スレッドになってるわけですよね? つまりスレッドに結びついた同期オブジェクトを使っても意味はないですよね? で本来ならメソッドが同時に実行されることはないはずですが、 さらにATLとかを呼び出すと、その時点でSTAのメッセージループに処理が戻って メソッドが再入可能になってしまうんではないのかということです。 アンマネージからでも、OCX経由?なら同じことになりませんか? |