search
LoginSignup
7
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

『応答なし』にならないアプリ開発

『応答なし』と表示される原因

処理開始から終了まで一定時間描画系のWindowsメッセージ(WM_PAINT等)が処理されないと『応答なし』が表示されます。
※レジストリの設定にもよりますが、現在主要Windows7以降の標準では5秒が標準となります。

主にシングルスレッドで重い処理(メソッド内で時間のかかる処理をしたりDBから大量のデータを取得等々)した時におこります。ループ処理で調子よく動いているように見えてもフォームをクリックとかしたら途端に『応答なし』になったりしますよね。

『応答なし』の回避策あれこれ

Application.DoEventsをコール

まず思いつくのが「DoEvents」。
便利な反面メッセージキューにある全てのWindowsメッセージが処理されるため、思いがけない動作をする可能性があります。
業務用アプリでは二度押し防止策等で動作中各種ボタンを非活性にする等することが多いですが、やはりすべての副作用的要素が取り除けない為、出来れば最終手段としたいものです。

Win32APIによるWindowsメッセージ処理

次にWin32APIの使用で、描画系のメッセージのみ処理する方法があります。
が、結果的にDoEvents同様各ボタンの非活性化などが必要になります。
(DoEventsより副作用は抑えられるはずですが・・・)

BackgroundWorkerにる別スレッド実行

ここからが本題ですね。
BackgroundWorkerを使用することで手軽にマルチスレッド処理が実現できます。
注意点としてはワーカースレッドからコントロール等スレッドセーフでないものに直接アクセスしないことでしょうか。処理の中でコントロールへアクセスする必要がある場合、Invokeを用いるか、BackgroundWorkerのProgressChanged・RunWorkerCompletedイベントのようなメインスレッドで実行される部分でする必要があります。

以下簡単なサンプルになります。

・Form1にボタン・プログレスバー・ラベルを一個ずつ配置

Form1.vb
Imports System.ComponentModel
Public Class Form1
    Private MAX_COUNT = 100
    Private WithEvents bgWorker As New BackgroundWorker

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        ProgressBar1.Hide()
        Label1.Hide()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ProgressBar1.Maximum = MAX_COUNT
        ProgressBar1.Value = 0
        ProgressBar1.Step = 1
        Label1.Text = String.Empty

        '*** 進行状況の報告を有効に
        bgWorker.WorkerReportsProgress = True
        '*** 処理実施
        ProgressBar1.Show()
        Label1.Show()
        bgWorker.RunWorkerAsync()
    End Sub

    ''' <summary>
    ''' BackgroundWorkerの処理部
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub bgWorker_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgWorker.DoWork
        For i = 1 To MAX_COUNT
            '*** 重い処理
            Threading.Thread.Sleep(500)
            '*** プログレス処理
            bgWorker.ReportProgress(i)
        Next
    End Sub

    ''' <summary>
    ''' プログレス処理
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub bgWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bgWorker.ProgressChanged
        ProgressBar1.PerformStep()
        Label1.Text = String.Format("({0}/{1})", ProgressBar1.Value, MAX_COUNT)
    End Sub

    ''' <summary>
    ''' BackgroundWorker処理完了時
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub bgWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
        ProgressBar1.Hide()
        Label1.Hide()
    End Sub
End Class

BackgroundWorker等別スレッドで実行する際もDoEvents同様各ボタンの非活性等で同時実行を制御する必要があることに気を付けてください。

次のステップとして、Task(Async/Await)がありますが、長くなっているので次回に(。-`ω-)b

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
7
Help us understand the problem. What are the problem?