none
排他ロックされた場合の待つ? RRS feed

  • 質問

  • お世話になっております。

    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

    2017年3月23日 11:11

回答

  • 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以降ぐらいに移行すれば、さらに簡潔に記述できるマルチスレッドライブラリも利用できます。

    2017年3月23日 12:40

すべての返信

  • 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以降ぐらいに移行すれば、さらに簡潔に記述できるマルチスレッドライブラリも利用できます。

    2017年3月23日 12:40
  • ご回答ありがとうございます。

    返信遅くて申し訳ありません。

    マルチスレッドの使い方が、いまいち分かっていないです。

    ただ、今回のソフトを再度確認してみましたが

    Button2_Clickは、排他ロックされました。

    そして、roopをButton2_Click_1で停止させると

    Button2_Clickによるroopがされて、メッセージがでましたので

    SyncLockにより、待たされる動作が確認できました。

    質問投稿前は、Button2_Clickは無視されてなかったもののような動作な気がしましたが

    気のせいだったようです。

    ありがとうございました。もっと勉強してみます。

    2017年3月25日 1:44
  • Hongliangさんの説明が理解できていないでしょうか?

    挙げられたコードはシングルスレッドで動作しています。各メソッドでThread.CurrentThread.ManagedThreadIdの値を確認してみてください。常に同じ値が得られるはずです。

    その上でSyncLock文はスレッド間の排他は行いますがスレッド内では排他しません。つまり同一スレッドであればSyncLockは何度でも入ることができ、排他されることはありません。

    # それはそうとroopでなくloopです…。

    2017年3月25日 10:10
  • 皆様、いつもありがとうございます。

    理解してませんでした。

    つまり、排他された訳ではないことですね。

    では、現在実行中のメソッドを

    もう1回呼び出すと、何が起こっているのでしょうか?

    オブジェクトとしては一つなので、やはり、待つという動作と考えてよいのでしょうか?

    また、Formのタイマーイベントから呼び出しても同一スレッドですか?

    roop・・・・すいません。

    確かに、、恥。。

    2017年3月27日 1:43
  • 再帰呼び出しと同じで、指示通り何事もなく実行されます。
    2017年3月28日 22:37