スレッドの終了処理について

[戻る]
tokuchan 2002/04/08(月) 17:05:12
初めて投稿させて頂きます。

環境はWin2000 Pro(SP2),VC++6.0(SP5)です。
複数のスレッドが走っている最中に「キャンセル」ボタンを押すと全てのスレッドを終了
させる処理を行いたいのですが、調べた限りでは、

1.各スレッド内の処理のいくつかのタイミングでWaitForSingleObject()を埋め込み、
返り値により、処理を場合分けを行いスレッドを終了する。
2.「キャンセル」ボタンが押されたらSetEvent()でシグナルON

という方法があるようなのですが、私の場合、各スレッド内の処理でデータベースにSQL文を
送っているのですが、この処理が終わって返ってくるまでの時間が非常に長い場合があるので
「キャンセル」を行えるようにしたいのです。従って、1.のスレッド内のいくつかのタイミング
でWaitForSingleObject()を行っても意味がないようです。
データベースとの接続はODBCを使っています。データベースはPostgreSQLです。

terminateはメモリリークが発生するので、なるべく使いたくないのですが、
どなたか良い知恵をお持ちでしたらご教授願います。
宜しくお願い致します。

TAD [E-Mail] 2002/04/08(月) 19:21:57
キャンセルボタンが押された後、他のスレッドが終了するまでの間、マウスカーソルを砂時計カ
ーソルにするというのはどうでしょうか?
私は、ファイルコピーをマルチスレッドで作ったことがありますが、大きなファイルをコピー中の
時は処理が戻らず、キャンセルボタンが押されたと同時にスレッドを終了させることができませ
んでしたが、ウェイトカーソルを使うことによって、ユーザーにキャンセル処理中であることを知
らせてやることで解決としました。
SQLに関しては詳しくないのですが、このような妥協案ではだめでしょうか。

tokuchan 2002/04/08(月) 19:56:55
TADさん解答ありがとうございます。
現在、一応データベースにアクセス中である旨のダイアログは表示させておりまして、
何か動いていないとさみしいので、aviムービーも再生させています。
プログレスバーも考えたのですが、処理がサーバーに行ってしまっているため、
進捗状況がつかめないので無理でした。
しかし、データ量が多いと5分以上も待たされることもあるため、何とかして
終了処理を行えればと思っています。

宜しくお願い致します。

けみ 2002/04/08(月) 22:30:24
まずお断りです。解決方法ではありません。ごめんなさい。
自分で試したわけでもないです。

スレッドを作る前にCDatabaseオブジェクトを作成して、
スレッドに渡します。呼び出し元のほうで、作成したCDatabase
オブジェクトをクローズしたらどうなっちゃうんでしょうか?

でエラーとか、例外が発生したらラッキー?
まさに初心者的発想。みなさん突っ込んで頂いてかまいません。
ごめんなさい。(でもやさしくお願います)

セッションを切る方法があるといいんですけど、分かりません。

sugar 2002/04/09(火) 09:40:02
DBとのコネクションを破壊することになるんでしょうが、危険です。
エラーや例外程度で済めばいいですが、処理の内容次第では、
DBにロックがかかったままとか、DB自体を傷めてしまう危険性があると思います。
お勧めできません。

> データ量が多いと5分以上も待たされる
>
5分以上はきついですね。
1つのSQL(SELECT文)でこれだけ時間がかかるということですか?
であれば、処理中の中断は諦めた方がよいのではないでしょうか。
もっと処理効率をよくする余地はないのですか?

dairygoods 2002/04/09(火) 10:46:15
CDatabase::Cancel() は、使えないでしょうか?
ヘルプを見ると、単に呼び出すだけでは使えないようで、
DBアクセスを非同期で実行させる必要があるらしいです。

tokuchan 2002/04/09(火) 10:51:59
けみさん、sugarさん、ご解答ありがとうございます。

私もセッションを強制的に切ってしまえばよいのではないか?
というアイデアが浮かんだには浮かんだのですが、具体的な方法
が思いつきませんでした。
実は、データベースとのやり取りの部分はActiveX.dllでやっておりまして、
各スレッドがその中の関数を呼び出していますので私のケースでは、けみさんの
方法が使えません。
しかし、sugarさんの申されるようにDBを壊す可能性があるとなると
やはり危険ですよね。

処理時間に関してですが、5分という時間ですが、これはSQL文1つでは
ありません。現在テストデータを使ってですが、大体一つのSQL文で2,30秒というところです。
しかし、本番の環境によっては更にデータ量が増えることも充分予想されるので更に時間が
かかることもあり得ます。
マルチスレッドにしているのは複数のデータベースサーバとの通信があるためです。
現在の処理としては、SQL文を発行する前後でキャンセルフラグが立っていないかどうかを
見に行っているので、最悪一つのSQL文の待ち時間程度に抑えられているはずです。
説明不足で申し訳ありません。
やはりここら辺で妥協せざるを得ないのでしょうか...。

tokuchan 2002/04/09(火) 12:07:56
dairygoodsさん、ご解答ありがとうございます。
時間が重なってしまったようですね。

私もMSDNを見てみました。
直前の発言でもあるようにデータベースとの接続部分は
VBのActiveX.dllで作ってあります。
接続、取得にはVBのADODB.Connection、ADODB.Recordsetクラスを使ってあるのですが、
これら2つのクラスでもCancelというメソッドが確認できました。
やはり、dairygoodsさんの申されるように非同期実行することが前提条件のようです。
確かにこのCancelメソッドは使えそうです。安全なのかどうなのかはよくわかりませんが。

となると、まずけみさんの申されるようにヒープ領域に変数を宣言しておいてそれを
各スレッドに渡し、「キャンセル」されたらヒープにある全てのCDatabaseないし
CRecordsetのインスタンスでCancelメソッドを実行すればいけそうな気がします。
ActiveX.dllの処理は全てVCに持ってくる必要がありそうですが。

ところで、DBの非同期実行とはいかなるものなのでしょうか?
何分、実務経験は1年もなく、データベースもマルチスレッドも今回が初めてという初心者
なのでお教え願えれば幸いです。

クリリン 2002/04/09(火) 12:58:30
マルチスレッドとは関係ありませんが、
>一つのSQL文で2,30秒
ですよね。おそらく、VCからSQLスクリプト文を加工しながら処理してるのでしょうか?
もし、そうであれば、ストアドプロシージャにパラメーターを渡してDBを操作する事をお奨め
します。
たしか、PostgreSQLにはストアドプロシージャ機能が有ったはずです。
ストアドプロシージャであれば、
「データ検索->更新」、「データ検索->ヒットなし->追加」といった処理をDB内部で実行し
てくれますので、処理スピードの向上、およびプログラム側からのDB操作関連のスクリプトを
書き直すなどが不要になります。

また、DBを強制的に切ることになる事を前提に言いますと、SQLスクリプトを実行する前に
[BeginTrans]コマンドを実行し、ロールバックできるようにした方がいいですね。そうした場
合、本当の意味でデータ処理をキャンセルする事ができます。
しかし、DBを強制的に切ることは私もお奨めできません。

>Cancelメソッドは使えそうです。安全なのかどうなのかはよくわかりませんが。
ADODBでのCancelメソッドはDBを強制的に切ることよりは安全です。
このCancelメソッドの時に[RollbackTrans]コマンドを実行すると、
[BeginTrans]から[RollbackTrans]までのDB処理をキャンセルする事が可能です。
ちなみに実行は[CommitTrans]です。

マルチスレッドについてですが、「コンポーネントおよびコントロールギャラリ」の「プログレスダイアログ」を挿
入して、プログレス処理の所を削除し、アニメーションだけを表示する等をしたら簡単です。マ
ルチスレッド処理のコードが著しく減ると思います。


あまり参考にならなかったらすいません。

tokuchan 2002/04/09(火) 15:32:44
クリリンさん、ありがとうございます。

ストアドプロシージャに関しては知識がなかったので、多少自分なりに調べてみました。
処理速度の向上というのは私もやらなければならないと思っていましたので、
非常に参考になりました。ありがとうございました。

それと、皆さん知っておられるのかもしれませんが、
DBの非同期実行について調べてみたところ、どうやらこういうことのようです。
通常SQL文を送ると、クライアント側はサーバーからの結果を待ちますが、
非同期実行でOpenした場合は、サーバーにSQL文を送った後にクライアント側は
結果を待たずに次の処理に移るということみたいです。
つまり非同期実行で、SQL文を送ったあとにすぐループを作ってその中で、
サーバーから値が返ってきたかのチェックとキャンセルフラグが立っていないかのチェック、
それと、VBでいうDoEventsみたいなのをからませれば、私が思うような処理が
実現できそうです。

というわけで、みなさんのおかげでかなり現実味が帯びてきました。
いろいろありまして、すぐに取り掛かれるかはわかりませんが、
うまくいきましたら報告させて頂きます。
みなさん、本当にありがとうございました。

tokuchan 2002/04/09(火) 17:44:03
[[解決]]
みなさん、ありがとうございました。
思ったよりも速くできました。
こういったところに投稿したのは初めてだったのですが、本当に投稿して良かったです。
これからも宜しくお願い致します。

次はパフォーマンスの改善をやりたいと思います。

けみ 2002/04/09(火) 22:29:05
私も勉強になりました。皆さんありがとうございます。
解決しているのにレスを書いてしまって申し訳ありません。
今後ともよろしくお願います。

古いスレッドには追加発言できません。