- PR -

アンマネージドとマネージドのスレッド対応

投稿者投稿内容
ちゃーはん
会議室デビュー日: 2007/06/25
投稿数: 13
投稿日時: 2007-06-26 17:03
とっちゃんさん、ありがとうございます。
引用:
Managed なEXEのスレッドモデルとOCXのスレッドモデルは一致していますか?

スレッドモデルが一致していないと、スレッド処理はグダグダになりますよ。
排他制御や同期処理というあたりはとくに...

OCX側は何もしていなければ、Apartment(STAと同じ)だと思いますので、Managed のスレッドモデルもSTAになっていないと上手くいかないとおもいます。


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に設定というのはやり方が間違っているのでしょうか?
自分の知識不足で、教えて頂きたいことばかりで申し訳ありませんが宜しくお願いします。


れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-06-26 17:12
引用:

同一スレッド上ではミューテックスやクリティカルセクション、セマフォなどの排他・同期は効かずに、そのままDLLからATLサービスEXE側のメソッドが連続して呼ばれてしまいます。
このため前の処理が終わらないうちに次の処理が動き、後の処理中に前の処理のデータクリアが呼ばれてしまい、後の処理の途中でデータが空っぽになるとう障害で困っています。



同一スレッドでメソッドが終わる前に同じメソッドが呼ばれる、
というのはメソッドで再帰が起こっている場合意外、
ありえません。

メソッドで再帰がおきているなら、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/25
投稿数: 13
投稿日時: 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で抜けている状態です。(ログ等で確認しました)
実際にアンマネージドやマネージド拡張ではちゃんと待つのでコードとして問題ないと思っていましたが、どこかマズい部分があれば、申し分かりませんが教えて頂ければと思います。
宜しくお願いします。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-06-26 18:56
引用:

ちゃーはんさんの書き込み (2007-06-26 18:22) より:

しかしVB.NETのマルチスレッドで呼ぶと、先に走っているGetDataのReleaseMutexが呼ばれる以前に、後からのGetDataのWaitForSingelObejctがWAIT_OBJECT_0で抜けている状態です。(ログ等で確認しました)



VB6のコードをコンバートしたりOCXつかって呼び出したりというのは
とても試せませんが、
ATL呼び出してもCOM呼び出しても、
私のところでは、Mutexはちゃんと動いてます。

GetDataをマルチスレッドで2回呼び出した場合、
2回結果がもどっていますか?

ログをとってみたとき、
同じスレッドIDで全てのメソッドが2回よばれていますか?
呼ばれているなら、その順番はどうなっていますか?

エラー処理はどうしていますか?
VBにエラーが戻ってきていませんか?

なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 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 ]
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-06-27 03:33
引用:

なちゃさんの書き込み (2007-06-27 01:23) より:
というか、OCXがSTAならそもそもMutexでの排他自体に効果がないですね(1プロセス内



?!
そうなんですか?

名前なしMutexならロック範囲がプロセス内なので
STAで効果ないのもわかりますが、
名前有りMutexなのでシステムグローバルに効くと思うのですが。

ちゃーはん
会議室デビュー日: 2007/06/25
投稿数: 13
投稿日時: 2007-06-27 09:22
れいさん、なちゃさん、お返事りがとうございます。
みなさん、お忙しいところ再々アドバイスを頂き、感謝、感謝です。

引用:

れいさんからの引用
GetDataをマルチスレッドで2回呼び出した場合、
2回結果がもどっていますか?


はい、戻ってきます。

引用:

れいさんからの引用
同じスレッドIDで全てのメソッドが2回よばれていますか?
呼ばれているなら、その順番はどうなっていますか?


はい、同じスレッドIDです。(DLL側でGetCurrentThreadIDで取得した結果です)
シーケンスとしては先発のGetDataメソッドの途中で後発のGetDataメソッドが走り始め、
その後発のメソッドのデータ転送処理中に先発のメソッドのデータクリア処理が動き、データ転送の途中でデータが空っぽになり、データ転送メソッドがエラーを返し、結果的に後発GetDataメソッド自体がエラーを返すということになってしまっています。

引用:

れいさんからの引用
エラー処理はどうしていますか?
VBにエラーが戻ってきていませんか?


DLLのメソッドでは、実際は排他処理を含め、全て戻り値やtyr〜catch、その他でエラー判定を行っており、エラー値を呼び元のAPのほうに返しています。
現在は上記に書いたようにデータ転送で、データが空っぽのエラーを返してきています。

引用:

なちゃさんからの引用
というか、OCXがSTAならそもそもMutexでの排他自体に効果がないですね(1プロセス内なら)…
OCXをMTAにできる?のならMutex等で普通に排他できると思いますが…


確実なことはいえませんが、今のOCXは多分STAなのかなと思います。
DLL側で(今回の件と関係ないですが)、ある別のATLのサービスを使っていますが、アパートの問題でMTAなら一度Openすればあとはそのままでよし、STAならば毎度Openするという使い分けの部分があり、OCX(VB)から呼ばれた場合は無条件にSTAでの扱いになっています。
もともとVB6用のOCXですから、VB6の場合は無条件でSTAでも問題なかったのかなと・・・
そういったことの確認方法や、もしOXCがSTAならMTAにする方法とかが調べているのですがまだ分かりません。
もしご存知なら教えていただければと思います。

引用:

なちゃさんからの引用
名前なしMutexならロック範囲がプロセス内なので
STAで効果ないのもわかりますが、
名前有りMutexなのでシステムグローバルに効くと思うのですが。


今回の件と全く同じ現象かは分かりませんが似たような話題がありました
http://forums.belution.com/ja/vc/000/404/25.shtml
http://forums.belution.com/ja/vc/000/404/33.shtml
といっても解決策ははっきりしてないようですが・・・

勝手なお願いばかりです申し訳ありませんが宜しくお願いします。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-06-27 11:55
Mutexがシステムグローバルかとかそういうことではなくて、
OCXがSTAなら、このメソッドに入ってきた時点で必ず同一スレッドになってるわけですよね?
つまりスレッドに結びついた同期オブジェクトを使っても意味はないですよね?

で本来ならメソッドが同時に実行されることはないはずですが、
さらにATLとかを呼び出すと、その時点でSTAのメッセージループに処理が戻って
メソッドが再入可能になってしまうんではないのかということです。

アンマネージからでも、OCX経由?なら同じことになりませんか?

スキルアップ/キャリアアップ(JOB@IT)