トップ回答者
排他ロックされた場合の待つ?

質問
-
お世話になっております。
VB2008.NETを使用しておりますが
以下のコードのような感じで排他制御を入れております。
roopで延々とループされたところに
Button2のクリックイベントでroopに
を実行させると、排他ブロックされますが
クリックイベントはroopの実行を諦めて
ハンドラー自体は終了となります。(Messageが表示されない)
SyncLockは処理を待たせると思っておりましたが
待たずに、Exitされるようです。
しっかりとroopが実行できるまで待つ方法はないでしょうか?
ご教示よろしくお願いします。
Public Class Form1 Private obj As New Object Private stop1 As Boolean = True Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click stop1 = True roop() End Sub Private Sub roop() SyncLock obj While stop1 = True Static i As Integer i += 1 Application.DoEvents() Console.WriteLine(i.ToString) End While End SyncLock End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click roop() MessageBox.Show("") End Sub Private Sub Button2_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click stop1 = False End Sub End Class
回答
-
Application.DoEvents()を呼び出すと一部においてマルチスレッドのような挙動を示しますが、実際にマルチスレッドで動くわけではありません。SyncLockは同一スレッドからの呼び出しとなり、排他ロックは行われません。
まずButton1_Clickが実行されると、ループが開始されます。ループ内でApplication.DoEventsが呼び出されているので、他の入力があった場合はそれの応答処理が行われることになります。
この状態でButton2_Clickをクリックすると、roopメソッドが呼び出され、先述のように排他ロックされずにループに入ります。このループが終わり、Button2_Clickから抜けるまではButton1_Clickが実行している処理は待たされます。
で、Button2_Click_1が実行されるのであれば、Button2_Clickによって開始されたループが終了してメッセージボックスが表示され、メッセージボックスを消したあとButton1_Clickによって開始されたループが再開され(ると同時に終了条件を満たしてループが終了し)ます。
Application.DoEvents()はマルチスレッドを扱えなかった昔のプログラム環境では有用だったかも知れませんが、現在のVBでは問題なくマルチスレッドを扱える環境になっています。Application.DoEvents()は存在ごと忘れて、マルチスレッドの勉強を行うことをお薦めします。
VS2008だとBackgroundWorker辺りでしょうか。この開発環境も今となっては相当古いので、VS2015以降ぐらいに移行すれば、さらに簡潔に記述できるマルチスレッドライブラリも利用できます。
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年3月24日 0:42
- 回答としてマーク to1109 2017年3月25日 1:44
すべての返信
-
Application.DoEvents()を呼び出すと一部においてマルチスレッドのような挙動を示しますが、実際にマルチスレッドで動くわけではありません。SyncLockは同一スレッドからの呼び出しとなり、排他ロックは行われません。
まずButton1_Clickが実行されると、ループが開始されます。ループ内でApplication.DoEventsが呼び出されているので、他の入力があった場合はそれの応答処理が行われることになります。
この状態でButton2_Clickをクリックすると、roopメソッドが呼び出され、先述のように排他ロックされずにループに入ります。このループが終わり、Button2_Clickから抜けるまではButton1_Clickが実行している処理は待たされます。
で、Button2_Click_1が実行されるのであれば、Button2_Clickによって開始されたループが終了してメッセージボックスが表示され、メッセージボックスを消したあとButton1_Clickによって開始されたループが再開され(ると同時に終了条件を満たしてループが終了し)ます。
Application.DoEvents()はマルチスレッドを扱えなかった昔のプログラム環境では有用だったかも知れませんが、現在のVBでは問題なくマルチスレッドを扱える環境になっています。Application.DoEvents()は存在ごと忘れて、マルチスレッドの勉強を行うことをお薦めします。
VS2008だとBackgroundWorker辺りでしょうか。この開発環境も今となっては相当古いので、VS2015以降ぐらいに移行すれば、さらに簡潔に記述できるマルチスレッドライブラリも利用できます。
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年3月24日 0:42
- 回答としてマーク to1109 2017年3月25日 1:44
-
ご回答ありがとうございます。
返信遅くて申し訳ありません。
マルチスレッドの使い方が、いまいち分かっていないです。
ただ、今回のソフトを再度確認してみましたが
Button2_Clickは、排他ロックされました。
そして、roopをButton2_Click_1で停止させると
Button2_Clickによるroopがされて、メッセージがでましたので
SyncLockにより、待たされる動作が確認できました。
質問投稿前は、Button2_Clickは無視されてなかったもののような動作な気がしましたが
気のせいだったようです。
ありがとうございました。もっと勉強してみます。