teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

修正されたコードに対する回答を追記

2016/08/14 07:06

投稿

flied_onion
flied_onion

スコア2604

answer CHANGED
@@ -52,4 +52,81 @@
52
52
  ---
53
53
 
54
54
  毎回1分待機させて終了させないBackgroundWorkerなら最初の指摘を直せば動きそうな気がします。
55
- ただそういう場合、1分おきに実行はTimerに任せて、BackgroundWorkerは毎回終了するようにした方が良いように思います。
55
+ ただそういう場合、1分おきに実行はTimerに任せて、BackgroundWorkerは毎回終了するようにした方が良いように思います。
56
+
57
+ ---
58
+
59
+ **追記:8/14 15:14**
60
+
61
+ 提示のコードで試してみました。処理としては、e.Resultにlstをセットして、RunWorkerCompletedで受け取っているので、そこは問題ない様に見えます。
62
+ 後述する「DoWorkで使っているlst変数が宣言されていない」(frmMainのものを使ってしまっていて状態が不明瞭に)という問題がありますが、最終的にはclsMainのprepareで何件のリストを返しているかによるので、そこの実際の処理を見直してみるしかなさそうです。
63
+
64
+ `txtBox?`の数字がbeanのNoときちんとあっているかは試してません。
65
+ Controls.Findとその次の行をコメントアウトして動くなら、受け渡しの問題ではありませんね。
66
+ ```vb
67
+ For Each r As bean In lst
68
+ 'Dim rResult As Control() = Controls.Find("txtBox" & r.No, True)
69
+ 'CType(rResult(0), TextBox).Text = r.Result
70
+ Next
71
+ ```
72
+
73
+ 長くてすいませんが、気になる点を指摘してみます。
74
+
75
+ まずは軽微なところから
76
+
77
+ * Cancel処理で正常終了
78
+ ここは今のところの工夫ということで後回しで良いですが、CancelAsyncを読んだらキャンセルされて欲しいので、中断/終了は別の方法をとった方が良いでしょう。これは後で書きます。
79
+ * beanのプロパティと内部変数名が同じ名前
80
+ VB.NETは大文字と小文字を区別しないので、privateのnoとPropertyのNoが二重定義でエラーになります。
81
+ これは質問用に書き換えたときのミスだろうと思うので。
82
+ * 変数CLSMainとCLSListMain
83
+ 質問に記載するときのミスなら問題ありませんが、そうでないなら問題です。
84
+ ```vb
85
+ ' DoWork内
86
+ Dim CLSMain As clsMain = New clsMain ’CLSMain 変数を用意
87
+ lst = CLSListMain.prepare() 'CLSListMainとは?
88
+ ```
89
+ * RunWorkerCompleted内でのCancelAsync
90
+ ` If e.Cancelled = True Then`の次の行
91
+ これは単純に不要です。ここに入ったときはすでにキャンセルされているので。
92
+
93
+ その他の問題となりそうなところ
94
+
95
+ * Threading.Thread.Sleep(60000)
96
+ これだとWhile無いが1回実行されるたびに 1分待つことになります。
97
+ 現在の問題と関係ないので…と思ってましたが実際に書いてみたら理由がわかった気がします。
98
+ これがないと非同期なはずなのに固まるから入れたんではないでしょうか。
99
+ もしそうなら固まる理由はReportProgressを呼びすぎていることにあります。
100
+ ReportProgressを実行する間は画面側の処理になりますので、これを過剰に呼ぶということは、裏方(別スレッド)に任せたのに、状態を確認しにいって何もできてない状態です。
101
+ ウェイトを入れるか、ReportProgressを呼ぶ条件をつけたほうがいいでしょう(カウンタを用意しておいて100回に1回だけ呼ぶなど)。
102
+ 今は、Thread.Sleep(300)ぐらいにしておけばいいと思います。実際のClsMainがなにしているのかにもよりますが。ここでSleepしている=作業用スレッドは止まっているということに注意してください。
103
+
104
+ * DoWorkで使っているlst変数が宣言されていないのと List(Of bean) をnewしすぎ。
105
+ 結論から言えば、lst( List(Of bean) )のNewはclsMainのprepare()でnewしているので他は必要ありません。
106
+ DoWork内では変数を用意すればいいだけです。
107
+ 今の作りだとWhile Trueのすぐ前の行に用意すればいいかと。
108
+ ```
109
+ Dim lst As List(Of bean)
110
+ While True
111
+ ```
112
+ そして、frmMainの先頭の private lst ~ の行は不要になりますので削除します。
113
+ スレッド内(DoWork内)では、スレッド外部で宣言されている変数はあまり触らないようにします(Threadとの連携のために用意したものならいいですが、DoWorkでもfrmMainでも同時に変更したとき厄介だからです)。
114
+
115
+ * clsMainでのList(Of bean)の初期化はただしいか。
116
+ 先ほど、clsMain内だけ List(Of bean) のnewを残しました。
117
+ 実際の処理次第ではありますが、prepareでListを毎回newしてしまうのでなにか外部のデータでもない限り、その都度Listは空になります。prepare内でアイテムを追加してますので、その回で追加されたアイテム分しか結果としてのこりません。
118
+ そこでデータが変わらないならWhileでループしても何回やっても同じになってしまうなと感じました。時刻によってデータが変わったり外部との連携があるなら問題ないのです。
119
+ ただ、そうであればそのWhileループの回で、追加するものがなにもなければ結果は 0件になります。
120
+
121
+ ---
122
+
123
+ * キャンセルについて
124
+ これは当面の問題が解決してから気にしてください。
125
+ 中止という意味でキャンセルさせたいとき、DoWork内で e.Cancelと Trueにセットすると、
126
+ RunWorkerCompleted内で、CancellationPendingがTrueになります。
127
+ キャンセル処理としてはこちらの方が素直な作り方になります。
128
+ 終了の条件を別途設ける(frmMainのprivate変数にboolean変数を用意しておくとか、RunWorkerAsyncに連携用オブジェクトを渡しておいて、DoWorkEventArgsから受け取る(結果をe.Resultから受け取るように)とか)必要がありますので、今の問題が片付いてから検討してもいいでしょう。
129
+ 単に終了させるのは、e.CancelにFalseをセットした状態でWhile文を抜ければいいだけです。
130
+ キャンセルを終了に流用してるだけなので、そこまで悪いことではないですが、「CancelAsyncよんだけどキャンセルロジックに入らない」というのは後で見るとわかりにくいかもしれません。コメントだけでも残しておくといいでしょう。
131
+
132
+