- PR -

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

投稿者投稿内容
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2007-06-27 13:41
引用:

つまりスレッドに結びついた同期オブジェクトを使っても意味はないですよね?



ん、どれが?

名前つき Mutex 作る時の名前って、スレッドIDとかから生成してるんでしたっけ?
それならずっこける可能性は大有り。

引用:

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



この辺はやや意味不明。

呼び出し先の方で意図的にメッセージポンプを回さない限り、最上位の呼び出しは保存されていると思いますよ。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-06-27 15:23
引用:

なちゃさんの書き込み (2007-06-27 11:55) より:
で本来ならメソッドが同時に実行されることはないはずですが、
さらにATLとかを呼び出すと、その時点でSTAのメッセージループに処理が戻って
メソッドが再入可能になってしまうんではないのかということです。



私にはなちゃさんの言うreentrantの意味がよく通じてませんが、
(reentrantって状態の話ではなくコードの書き方、質の問題では?)
どっかでメッセージポンプに処理が移ってるということですか。

STAってブロックされると「暗黙のうちに」
メッセージループまわすんでしょうか?
http://msdn2.microsoft.com/ja-jp/library/54z06b18(VS.80).aspx
この辺の話かな?

今回の件では同じスレッドで再入しているのは確実なようなので
reentrantなコードに変更すればOKなんでしょう。
mutexでDLL呼び出しを保護するんではなく、
すでに再入かどうかのフラグを保護するのがいいのかな。
STAならmutexも要りませんが。

>名前つき Mutex 作る時の名前って、スレッドIDとかから生成してるんでしたっけ?
>それならずっこける可能性は大有り。

マネージなMutexもアンマネージなMutexもスレッドID使ってないと思いますよ。
マネージMutexとアンマネージMutexで同期とれますから。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2007-06-27 17:39
引用:

マネージなMutexもアンマネージなMutexもスレッドID使ってないと思いますよ。
マネージMutexとアンマネージMutexで同期とれますから。



いやいや、内部実装ではなくて「このアプリケーションで」の話です。

名前つきミーテックスを作るコード片が示されていたような気がするんですが、その時に指定している名前にスレッドIDが含まれてたっけか?というお話です。
ちゃーはん
会議室デビュー日: 2007/06/25
投稿数: 13
投稿日時: 2007-06-27 17:54
れいさん、なちゃさん、渋木さん、お返事ありがとうございます。
自分のレベルが低くてみなさんのお話についていけないところもあるのですが(ホント、お恥ずかしいです・・・)
自分なりに確認したところまでお話しすると

VB.NETのAPでコールしているOCXのメソッドの中身を替えて
・DLLのメソッドをコールしてる
↓から
・MessageBoxを表示するだけ
 に変更しました。
ちなみOCXのこのメソッドでは排他は行っていません。
このOCXのメソッドをVB.NETのAPで今までと同様にマルチスレッドでコールしても
MessegeBoxは一つづつしか表示されませんでした。(スレッドIDは同じ値)
これはOCXがSTAだからということに起因するのかと思います。

更にこのOCXのメソッドを
・MessageBoxを表示するだけ
↓から
・@MessageBoxを表示する
 ADLLのメソッドをコールする(このDLLのメソッドは最終的にATLのメソッドをコールしています)
 に変更しました。
このOCXのメソッドをVB.NETのAPでマルチスレッドでコールすると
最初はMessegeBoxは一つづつの表示でしたが、途中で二つ同時に表示されました。(スレッドIDは同じ値)

またダミーのDLL(排他やALTを呼ぶとかはしていません)を作って、
このダミーDLLのメソッドをALTを呼ぶDLLのメソッドの替わりにコールするようにしてみましたが、
この場合だと最初の例と同じようにMessageBoxは一つづつしか表示されませんでした。

このことから
引用:

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


と関連するのかなとも考えています。

なんとなくですが、やっぱりOCXやDLLではどうしようもなくて
ALT側で排他のような処理を考えてやらなきゃいけないのかと思っています・・・
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2007-06-27 18:59
引用:

VB.NETのAPでコールしているOCXのメソッドの中身を替えて
・DLLのメソッドをコールしてる
↓から
・MessageBoxを表示するだけ
 に変更しました。



モーダルダイアログは独自にメッセージポンプを回してしまうので、OCX が元々使っているメソッド呼び出しのシリアライズに悪影響を及ぼすかもしれません。

メッセージボックスではなく、デバッグ出力に変えて検証するべきと思います。

引用:

これはOCXがSTAだからということに起因するのかと思います。



「OCX が STA」なのではありません。

「(通常)OCX はシングルアパートメントでしか動作できないので、OCX を扱うスレッドは STA 属性で COM ランタイムを初期化しなければならない」のです。

ちょっと気になったところがあるんですが、

引用:

このOCXのメソッドをVB.NETのAPで今までと同様にマルチスレッドでコールしても



「OCX のメソッドをマルチスレッドでコール」しているとありますが、これ、どうやってます?

OCX のメソッドは、その OCX のインスタンスを作成したスレッド以外から直接呼んではならないし、無理くり呼び出してもクラッシュすることが多いはずなんですが。

OCX のメソッドを別スレッドから呼び出すには、(COM の)マーシャリングを行わなければなりません。

引用:

このことから
引用:

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


と関連するのかなとも考えています。



であるとしても、現時点では ATL というライブラリに問題がある可能性よりは、作成物の設計や作りこみに何か潜在的な問題があったと考えるほうが妥当なんじゃないでしょうか。

# ATL がまったくのバグフリーかというと、決してそんなことはありませんが。


[ メッセージ編集済み 編集者: 渋木宏明(ひどり) 編集日時 2007-06-27 19:33 ]
Atata!!
常連さん
会議室デビュー日: 2007/05/22
投稿数: 20
投稿日時: 2007-06-28 04:09
引用:

「OCX のメソッドをマルチスレッドでコール」しているとありますが、これ、どうやってます?

OCX のメソッドは、その OCX のインスタンスを作成したスレッド以外から直接呼んではならないし、無理くり呼び出してもクラッシュすることが多いはずなんですが。

OCX のメソッドを別スレッドから呼び出すには、(COM の)マーシャリングを行わなければなりません。



OCXの呼び出し側はVB.NETで作成したマネージドなアプリケーションと言う話ですので、
RCWが勝手に(COM の)マーシャリングを行うので、この場合は大丈夫かと。


で、先になちゃさんが言われてますが、RPCによる再入(re-entrant)の可能性が一番高いと思います。
DLLからサービスを呼び出すところでRPC呼び出しが発生しているため、ここで再入していると思われます。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2007-06-28 08:08
引用:

OCXの呼び出し側はVB.NETで作成したマネージドなアプリケーションと言う話ですので、
RCWが勝手に(COM の)マーシャリングを行うので、この場合は大丈夫かと。



なるほど、確かに RCW の解説に

引用:

The runtime creates exactly one RCW for each COM object, regardless of the number of references that exist on that object. The runtime maintains a single RCW per process for each object. If you create an RCW in one application domain or apartment, and then pass a reference to another application domain or apartment, a proxy to the first object will be used. As the following illustration shows, any number of managed clients can hold a reference to the COM objects that expose INew and INewer interfaces.



とありますね。

# .NET ではスレッド跨ぎの呼び出しを避けてたので知らなかった (^^;
Atata!!
常連さん
会議室デビュー日: 2007/05/22
投稿数: 20
投稿日時: 2007-06-28 13:42
私の考えを整理すると・・・


・ロックが効かない問題
OCXは通常STAで動作するためOCXのメソッドを呼び出す場合、
必ずOCXを作成したスレッド上で動作する。同一スレッド上で動作するため、
名前無しMutexまたは名前が同じMutexではロックできない。

# ただし、正しくマーシャリングされていればの話
# VB.NETは正しくマーシャリングする


・再入(re-entrant)している問題
OCXを作成したスレッド(めんどくさいので以下STAスレッドと呼ぶ)以外から
OCXのメソッドを呼び出した場合、呼び出し元のスレッドはブロックされ、
呼び出しがSTAスレッドの実行キューに溜まる。
メッセージループが動いているなら、上記のSTAスレッドの実行キューから
処理が1つ取り出され実行が開始される。
この処理は OCX ⇒ DLL =(RPC)⇒ サービス というようにRPC経由で
サービスのメソッドを実行する。DLLからサービスの呼び出し(RPC呼び出し)が完了すると
RPCランタイムは戻り値等の情報をSTAスレッドの実行キューに溜める。
DLLからサービスの呼び出しが完了した後すぐにDLL側の処理に復帰してくれればいいのだが、
実際には、RPCランタイムはSTAスレッドの実行キューに先に溜まっている処理を実行する。
ここで先に溜まっている処理とは、最初に書いたSTAスレッド以外からのOCXのメソッドの呼び出しであり
これが再入の原因となる。

# 実際のところマルチスレッドなので再入とは言えないか。


# マルチプロセスでマルチスレッドなCOMのシステムを構築すると
# 上記のような現象には頻繁に遭遇するので、
# 典型的なCOMスレッド地獄と言えなくもないですなぁ。

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