C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[29]: スレッドプール処理のキャンセルについて [1]


(過去ログ 54 を表示中)

[トピック内 45 記事 (21 - 40 表示)]  << 0 | 1 | 2 >>

■30403 / inTopicNo.21)  Re[15]: スレッドプール処理のキャンセルについて
  
□投稿者/ 片桐 (117回)-(2008/12/22(Mon) 22:34:36)
何度も名前がでている片桐です

なかなか書き込む機会がとれなかったことを申し訳ないなぁとおもいつつ、レスなぞを……

とりえず、No.30383に書かれている要件を満たすための片桐的解釈でポイントでまとめてみますね。

1.スレッドプールにスレッドを登録すると、いつ処理が開始されるかはOS次第。
  処理されるスレッドの実体(関数)はCallBack呼び出しとなります。

という前提で

2.ユーザーがダイアログから「OK」を押したときだけ、スレッドプールにたまっていて未処理になっているスレッドをすべて中止したい

ということですよね?

結論から言えば、スレッドプールにたまっているスレッドをキャンセルする、ことはムリです。ですが、スレッドプールからCallBack呼び出しされたときに「自スレッド処理は何もしなくて良い」という判断を実装させることで、「呼び出されるけど処理せずに自スレッドを終了」させることは可能でしょう。

なので、まずは複数のスレッドから読み出される可能性がある処理可不可の判断に使う変数「A」をSharedでもちます。
スレッドプールへ溜め込み、監視するスレッドBでは、ユーザーのダイアログからAにフラグを立てます。
CPUの数が4個くらいで、スレッドの数もそう無い時にはInterlockを使っても良いですが、SyncLockブロックを使うと確実ですね。
スレッドプールから呼び出され処理する予定のスレッドCは変数Aを見て処理可能なら処理を継続し、不可ならそこで関数を終えます

これで、キャンセルの実装ができるはずです。

スレッドプールに投げた全てのスレッド処理が完了しているかどうかの監視は、以前私が書いた方法でも良いですし、同話題の流れの中でもっとわかりやすい方法を書いてくださった方もいらっしゃいましたし、そのあたりをご参考にされればだいじょうぶかと。

ポイントは、監視のキーとなる変数(処理可能不可能判断フラグや処理件数など)はできるだけSharedでもっておいて、書き込みや読み出しでInterlockedクラスやSyncLockブロックをつかっておくこと。処理は若干遅くなりますが、確実にスレッド間の値同期を図ることができます。

ご参考になりましたら、幸いです。
引用返信 編集キー/
■30415 / inTopicNo.22)  Re[19]: スレッドプール処理のキャンセルについて
□投稿者/ やじゅ (886回)-(2008/12/23(Tue) 01:41:34)
やじゅ さんの Web サイト
No30390 (らんぺるーる さん) に返信
> 「FormClosing」イベントに以下のように記載し、
> スレッド処理が終わるのをWaitしているのですが、
> スレッド側が動いていないように思います。

SleepによるWaitがうまくいかないなら
別案として

スレッド処理中に FormClosing が発生したら、
とりあえず 内部的にフラグだけ立てて
一度、FormClosing をキャンセルしておいて

スレッド処理が全て完了したら、本当にFormClose
すればいいかもね。
引用返信 編集キー/
■30418 / inTopicNo.23)  Re[16]: スレッドプール処理のキャンセルについて
□投稿者/ 渋木宏明(ひどり) (1001回)-(2008/12/23(Tue) 07:34:34)
渋木宏明(ひどり) さんの Web サイト
2008/12/23(Tue) 07:59:14 編集(投稿者)

何が問題なのか、やっと分かった。

「スレッドプールにたまっているスレッドをキャンセルできない」って書いてあるから、「スレッドプールから起こしたスレッドだろうが、Thred.Start() したスレッドだろうが、自発的に終了させることはできるだろ?」ってずーっと思ってた。

スレッドプールの「キューにたまっている実行依頼(=こいつはまだ実行されていないので「スレッド」ではない)がキャンセルできない」という話ですか。

であれば

> 結論から言えば、スレッドプールにたまっているスレッドをキャンセルする、ことはムリです。

ということです。

これで都合が悪いなら、自分でワーカースレッドを管理する仕組みを作るしかありません。

ただ、往々にして

> ですが、スレッドプールからCallBack呼び出しされたときに「自スレッド処理は何もしなくて良い」という判断を実装させることで、「呼び出されるけど処理せずに自スレッドを終了」させることは可能でしょう。

で十分間に合うはず。

それで間に合わないってのは、どんだけキューに詰め込んでだ?みたいな。


引用返信 編集キー/
■30434 / inTopicNo.24)  Re[17]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (50回)-(2008/12/24(Wed) 10:29:01)
返答ありがとうございますっ。
片桐さん本人にまで返答をいただき、恐縮です。

スレッドプールにたまっているスレッドについては、
処理がなにもされていないため、印刷要求はされていないので、
印刷キャンセル処理の必要がなく、アプリケーションを終了してしまえば、
プールにたまっているスレッドもなくなるため
あまり問題視はしていませんでした。

今回問題にしていたのは、実行中のスレッドで既に印刷要求を
しているものについて、印刷キャンセルをおこなわなければいけない
という点です。

片桐さん案を採用することで、staticなメンバに印刷キャンセルフラグを持たせ、
フラグがオンのときは(閉じるボタンを押された場合)
実行中のスレッドでは、印刷をキャンセルし、
スレッドプールにたまっているスレッドについては、何もせず処理終了
というのはコーディングできました。

最後の課題は、「FormClosing」イベントで印刷キャンセルフラグ
をオンにして、いくらまっても(Sleepを今は使用しています。)
スレッド処理がおこなわれないという点です。

特にデッドロックが発生するようなコードはスレッドにいれていないです。
理由はなんなのでしょうか…。
実行中のスレッドに対して「Join」を使用すればスレッドの処理がおこなわれるのですが、
スレッドプールでは「End Sub」にはいっているにもかかわらず、処理の完了が
帰ってきません。

やじゅさんの案を試してみようと思うのですが、
一度フォームclosingイベントをキャンセルすると、
もう一度ユーザに閉じるボタンを押してもらう必要があるのではないでしょうか…。

引用返信 編集キー/
■30436 / inTopicNo.25)  Re[18]: スレッドプール処理のキャンセルについて
□投稿者/ 渋木宏明(ひどり) (1004回)-(2008/12/24(Wed) 11:22:13)
渋木宏明(ひどり) さんの Web サイト
> 実行中のスレッドに対して「Join」を使用すればスレッドの処理がおこなわれるのですが、
> スレッドプールでは「End Sub」にはいっているにもかかわらず、処理の完了が
> 帰ってきません。

そういうもんじゃなかったっけ?

スレッドプールのスレッドはスレッドプールが管理していて、再利用されたはず。

で、ユーザプログラムがやってるのは「スレッドープルからスレッドを取り出してる(借りてる)」わけじゃなくて、「スレッドプールに対してワークアイテムを登録して処理を移譲している」だけでしょ?

「スレッド」と「処理」が一緒こたになったまま話が進んでたので、何が問題点なのか分らんかったです。




引用返信 編集キー/
■30437 / inTopicNo.26)  Re[18]: スレッドプール処理のキャンセルについて
□投稿者/ なちゃ (238回)-(2008/12/24(Wed) 11:26:08)
Joinは単にスレッド終了を待つだけで、Joinしてないから他のスレッドが動かないとかそういうことはありません。

見ている限りでは処理やキャンセル完了のフラグ管理もしくは判断が正しくないと予想されます。

現状の、投入数や完了数のカウントアップはどのように、どこでやってますか?
いろんな状況を考慮してきっちり管理しないと、こういうことは起こり得ます。

引用返信 編集キー/
■30440 / inTopicNo.27)  Re[18]: スレッドプール処理のキャンセルについて
□投稿者/ やじゅ (888回)-(2008/12/24(Wed) 11:47:21)
やじゅ さんの Web サイト
No30434 (らんぺるーる さん) に返信
> やじゅさんの案を試してみようと思うのですが、
> 一度フォームclosingイベントをキャンセルすると、
> もう一度ユーザに閉じるボタンを押してもらう必要があるのではないでしょうか…。
>

そんなことはないですよ、その為にフラグを立てるわけですから。

ユーザーが、閉じるボタンを押した。

closingイベント
まだ、スレッドプールが全部完了してないけど、
ユーザーが閉じるボタンを押したってことは覚えとくからフラグを立てて一旦キャンセルするよん。

スレッドプールが全部完了
ユーザーが閉じるボタン押したフラグが立っているから、Closeメソッドを実行

closingイベント
スレッドプールが全部完了してるから、そのまま終了

フォームを閉じて終了
引用返信 編集キー/
■30446 / inTopicNo.28)  Re[19]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (51回)-(2008/12/24(Wed) 12:21:44)
>渋木宏明(ひどり) さん
質問の仕方がへたくそだったかもしれませんっ。お手数をお掛けしました。

>なちゃさん
そうですよねぇ。Joinをおこなわなくてもスレッド側は動いているはずですよね。
ただ、デバックモードでスレッド側にブレークポイントを設けて、
メイン側がsleepしても、スレッド側が呼び出されないんです。
Joinメソッドをメイン側がおこなえば、スレッド側にデバックが移り、正常に動いています。

カウンタについては、スレッドメソッドに入ったときに未処理総数フラグに加算し、
スレッドメソッドのFinallyで処理済み総数フラグに加算しております。

>やじゅさん
closingイベントが終わって、スレッドプールが全部完了
のあと、「ユーザーが閉じるボタン押したフラグが立っているから、Closeメソッドを実行」
はどこでやるのでしょうかっ??スレッド側で自分が最後のスレッドか監視して、
最後の場合は、メインフォームに対してCloseメソッドを発行ですか??
引用返信 編集キー/
■30448 / inTopicNo.29)  Re[20]: スレッドプール処理のキャンセルについて
□投稿者/ 渋木宏明(ひどり) (1006回)-(2008/12/24(Wed) 12:28:19)
渋木宏明(ひどり) さんの Web サイト
> そうですよねぇ。Joinをおこなわなくてもスレッド側は動いているはずですよね。
> ただ、デバックモードでスレッド側にブレークポイントを設けて、
> メイン側がsleepしても、スレッド側が呼び出されないんです。
> Joinメソッドをメイン側がおこなえば、スレッド側にデバックが移り、正常に動いています。

どっかでデッドロックか何か起こしてそうな気もしますが…

> カウンタについては、スレッドメソッドに入ったときに未処理総数フラグに加算し、
> スレッドメソッドのFinallyで処理済み総数フラグに加算しております。

lock とかで保護してますよね?>カウンタ

引用返信 編集キー/
■30450 / inTopicNo.30)  Re[20]: スレッドプール処理のキャンセルについて
□投稿者/ なちゃ (239回)-(2008/12/24(Wed) 12:37:53)
単にデバッガ上の動きの問題か、あるいは実行とステップ実行で動作が違ったのを勘違いしてたとか、そういうことはないですか?
いずれにしても、普通に作ってればJoinしないと動かないなんてことはありません。

後は、フラグ変数をそれぞれどこで定義してるか、両方静的になってるか、それぞれ排他制御はちゃんとやってるか、即座に抜ける場合等でも矛盾が無いようにしてるか、辺りですかね。

フラグとスレッド管理?の部分だけソースを抜粋して載せてみた方がいいかも知れません。
引用返信 編集キー/
■30452 / inTopicNo.31)  Re[21]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (52回)-(2008/12/24(Wed) 12:51:23)
>渋木宏明(ひどり) さん
カウンタに加算する場合は、SyncLockを使用しています。

スレッド処理は、フォームのイベント中は待機していることはないですよね??
closingイベントにかかわらず、メインフォームのイベント処理中(そこから呼び出される
通常のメソッド中も同様です。)は実行されていないようです。

(今回の処理ですと印刷のスプールがおこなわれるので、イベント処理中に
スレッドが動いていないのが確認できます。)
引用返信 編集キー/
■30453 / inTopicNo.32)  Re[22]: スレッドプール処理のキャンセルについて
□投稿者/ なちゃ (240回)-(2008/12/24(Wed) 13:14:46)
一応念のため、デバッガで停止中は基本的に全スレッドが停止してます。
引用返信 編集キー/
■30454 / inTopicNo.33)  Re[22]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (53回)-(2008/12/24(Wed) 13:17:53)
>なちゃさん
ソースコードを掲載いたします。


スレッド印刷クラス
Public Class Print
    'メンバ変数
    Public Shared start As Integer = 0 'プール総数
    Public Shared end As Integer = 0 'プール処理済み数
    Public Shared batchCancelFlg As Boolean = False '何もしないか判定(終了時にオンにする。)

    'スレッドプールへの追加処理(メイン処理から呼ばれる)
    Public Sub Run
         ' ThreadMethodをスレッドプールで実行できるようにWaitCallbackデリゲートを作成
         Dim waitCallback As New System.Threading.WaitCallback(AddressOf BatchPrintRun)
         ' スレッドプールに登録
         System.Threading.ThreadPool.QueueUserWorkItem(waitCallback)
         start  = start  + 1
    End Sub

    '印刷処理(スレッドで実行する処理)
    Private Sub BatchPrintRun
         Try
             If batchCancelFlg = False Then
                  '印刷処理の実行
                  XXXXX
                  XXXXX
                  '印刷をキャンセルする場合
                  If  batchCancelFlg  = True Then
                      '印刷のキャンセル処理
                      XXXXX
                      XXXXX
                  End If
             End If
         Catch

         Finally
              SyncLock Me 
                   end = end + 1
              End SyncLock  
         End Try
    End Sub
End Class

'メインフォーム
Public Class Main
  'クロージングイベント
    Private Sub Main_FormClosing( ByVal sender As System.Object,  ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles 
                                  MyBase.FormClosing
            '印刷中の場合
            If Print.start <> Print.end Then
                '閉じますか?ダイアログでOKが選択された場合
                If showMsgDialog("XXX") = Windows.Forms.DialogResult.OK Then 
                    'スレッドの終了処理
                    Print.batchCancelFlg = True 'スレッドプールの処理を終了させる。
                    Do Until Print.start = Print.end 'スレッドプールの全ての処理が終わるまで待機する。
                        System.Threading.Thread.Sleep(1000)
                    Loop
                '閉じますか?ダイアログでキャンセルが選択された場合
                Else
           e.Cancel = True   
                End If
            End If
    End Sub

    '印刷要求(印刷ボタンが押された場合)
    Private Sub MainPrint_Click( ByVal sender As System.Object,  ByVal e As System.EventArgs) Handles MainPrint.Click
            Dim PrintInst As New Print
            PrintInst.Run
    End Sub
End Class


上記のように実装しています。(公開用に非常に簡素に書き換えていますがやっていることは同じです。)

引用返信 編集キー/
■30455 / inTopicNo.34)  Re[23]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (54回)-(2008/12/24(Wed) 13:20:27)
あ、スレッド処理の宣言が
「BatchPrintRun(stateInfo As Object)」の間違えです。
引用返信 編集キー/
■30458 / inTopicNo.35)  Re[22]: スレッドプール処理のキャンセルについて
□投稿者/ 渋木宏明(ひどり) (1007回)-(2008/12/24(Wed) 13:44:49)
渋木宏明(ひどり) さんの Web サイト
> カウンタに加算する場合は、SyncLockを使用しています。

減算する時も、読み取るときも保護しなくちゃダメです。(省略できる場合もあるけど、それがわかっている場合以外は全部保護するべき)

あと、カウンタの値は volatile 指定しないとダメ。

> スレッド処理は、フォームのイベント中は待機していることはないですよね??

ないですね。

> (今回の処理ですと印刷のスプールがおこなわれるので、イベント処理中に
> スレッドが動いていないのが確認できます。)

ソース見てないんで想像ですけど、ひょっとして、プリントスプールの操作?が

・そもそもワーカースレッドでやってはダメ
・メインスレッドがメッセージポンプを回してないと都合が悪い

なんてことはないですか?


引用返信 編集キー/
■30461 / inTopicNo.36)  Re[23]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (55回)-(2008/12/24(Wed) 14:15:16)
>渋木宏明(ひどり) さん
カウンタについては、了解です。今回減算はないので、読取時も保護してみます。
あ、volatileはVBにはなくないですか??

プリントスプールについては、もう少し調べてみます。

個人的に気になっていたのは、
MainPrint_ClickイベントでPrintクラスのインスタンスを作成して、
その中の「BatchPrintRun」メソッドでスレッド処理をしていますが、
MainPrint_Clickイベントが終わるとPrintクラスのインスタンスは破棄されてしまいます。
(スレッドの動きを見ると、インスタンス破棄後も
インスタンスのメンバ(Sharedではないメンバも含めて)は参照可能であり、
インスタンス自体が破棄されていないように見えるのですが…)

これは問題ないのでしょうか??

引用返信 編集キー/
■30561 / inTopicNo.37)  Re[24]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (56回)-(2008/12/26(Fri) 14:09:07)
>なちゃさん
ソースコードを載せてみましたが、
誤った箇所等あるでしょうか??
引用返信 編集キー/
■30566 / inTopicNo.38)  Re[24]: スレッドプール処理のキャンセルについて
□投稿者/ やじゅ (892回)-(2008/12/26(Fri) 15:35:17)
やじゅ さんの Web サイト
No30461 (らんぺるーる さん) に返信
> 個人的に気になっていたのは、
> MainPrint_ClickイベントでPrintクラスのインスタンスを作成して、
> その中の「BatchPrintRun」メソッドでスレッド処理をしていますが、
> MainPrint_Clickイベントが終わるとPrintクラスのインスタンスは破棄されてしまいます。
>

MainPrint_ClickイベントでPrintクラスのインスタンスを作成しては駄目でしょ。
Printクラスは、フォームのClosingで参照しているわけですから、インスタンスは
保持していないと。
でも、その割りにはClosingでエラーにならないのが不思議
引用返信 編集キー/
■30568 / inTopicNo.39)  Re[25]: スレッドプール処理のキャンセルについて
□投稿者/ らんぺるーる (57回)-(2008/12/26(Fri) 16:32:02)
フォームのClosingで参照しているのはSharedのメンバだけですので、
インスタンスはなくてもOKです。
引用返信 編集キー/
■30576 / inTopicNo.40)  Re[23]: スレッドプール処理のキャンセルについて
 
□投稿者/ まどか (598回)-(2008/12/26(Fri) 20:32:56)
関係なく単に気になったことを。

> Public Shared end As Integer = 0 'プール処理済み数
> SyncLock Me
> end = end + 1
> End SyncLock

Shared変数を操作するんだからロックオブジェクトもSharedにしなきゃいけないんではない?

Private Shared _LockObject As New Object
SyncLock _LockObject

引用返信 編集キー/

<前の20件 | 次の20件>
トピック内ページ移動 / << 0 | 1 | 2 >>

管理者用

- Child Tree -