|
| コントロールID | プロパティ名 | 設定値 |
|---|---|---|
| Button1 | (Name) | btnStart | Text | 開始 |
| RadioButton1 | (Name) | optDoEvents | Text | DoEvents 処理 |
| RadioButton2 | (Name) | optSingle | Text | パフォーマンス処理なし | checked | (true) |
| ListBox1 |
図 1. アプリケーション フォーム

btnStart をダブルクリックしてコードを表示します。「Windows フォームデザイナで生成されたコード」に続けて、アプリケーションの業務処理 (myProc) とリストボックスに記録する処理 (additem) を実装するため、以下のコードを追加します。業務処理に当たる myProc では、500ms (= 0.5 秒) のウェイトを持たせることで擬似的に重い処理を再現しています。処理を 1 回行うことに ListBox に記録をします。
Private Sub myProc()
Dim i As Integer
For i = 1 To 5
System.Threading.Thread.Sleep(500)
Me.additem(i.ToString() & "回目の処理完了 " & Now.ToLongTimeString())
Next
End Sub
Private Sub additem(ByVal strItem As String)
Me.ListBox1.Items.Add(strItem)
End Sub
続いて業務処理を起動するための処理を実装します。btnStart_click1 メソッドに以下のコードを追加します。ここでは、処理を開始する前にリストボックスを初期化し、ListBox に処理の開始を記録します。業務処理が終わったら処理の終了を記録します。
Private Sub btnStart_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnStart.Click
Me.ListBox1.Items.Clear()
Me.additem("start")
Me.myProc()
Me.additem("end")
End Sub

以上で、元になるアプリケーションの実装は完了です。では実際にビルトしてパフォーマンスのテストをしてみましょう。[ビルト] メニューから [ソリューションのビルト] を実行してアプリケーションをビルトします。問題がなければ、[Ctrl] キーを押しながら [F5] キーを押すか、[デバッグ] メニューの [デバッグなしで開始] をクリックします。
アプリケーションが起動したら、[開始] ボタンを押してみましょう。処理が開始されますが、リストボックスには何も表示されません。またウィンドウを移動しようとしてもできないことを確認してください。数秒たつと業務処理は完了しリストボックスには開始から終了までの記録が表示され、ウィンドウの移動も可能になります。
図 2. 基本アプリケーションを実行しても何も表示されない
いかがですか?
今回は 5 回の処理ですが、これが数百回の処理であった場合を想像してみてください。アプリケーションがハングアップしたのか、処理を続けているのか判別ができないでしょう。これではアプリケーションとしては問題がありますね。

では、ここに DoEvent 処理を追加しアプリケーションのパフォーマンスの改善を試みます。
アプリケーションのコードを表示し、MyProc メソッドに以下のコードを追加します。
Private Sub myProc()
Dim i As Integer
For i = 1 To 5
' 以下のコードを追加
If Me.optDoEvents.Checked Then
Application.DoEvents()
End If
' ここまで
System.Threading.Thread.Sleep(500)
Me.additem(i.ToString() & "回目の処理完了 " & Now.ToLongTimeString())
Next
End Sub
これにより、業務処理を実行する前に OS にたまっているイベントを実行することができます。

DoEvents を使った改修が完了したので、実際にビルトして効果を確認します。
アプリケーションが起動したら、DoEvents のラジオボタンを選択してから [開始] ボタンを押してみましょう。今度は開始メッセージが表示され次々と処理完了のメッセージがリストボックスに追加されます。このときにウィンドウの移動をしてみると (一呼吸遅れるかもしれませんが) ウィンドウを移動することができます。
図 3. DoEvents メソッドによって処理経過を確認できる
DoEvents メソッドを入れることで、イベントとしてためられていたリストボックスの表示処理や、ウィンドウの表示処理が実行されるため、少なくとも処理が続けられていることがわかります。また、スムースではないにせよウィンドウの移動などの処理も行うことができます。

では、ここで DoEvents 処理の問題を確認してみます。
Step 6 と同様に [開始] ボタンを押して処理を開始します。開始メッセージが現れたら、ウィンドウをドラッグしてウィンドウの移動をします。そのまま止めずにしばらく動かしてみてください。すると、アプリケーションの処理が進まずに途中で停止することが確認できるでしょう。
DoEvents を行った際にウィンドウの移動処理イベントがあった場合には、それを実行します。そしてそのウィンドウの移動処理が絶え間なく発生するため、いつまでたっても DoEvents 処理を終了することができないのです。つまりこの対処方法ではアプリケーション以外の処理の負荷に影響を受けやすいという欠点を持っているのです。
次に myProc の Sleep の時間を 500 (ms) から 5000 (ms) に変更して実行してみましょう。これは業務処理がさらに重く時間のかかる処理になったことを意味します。
Private Sub myProc()
Dim i As Integer
For i = 1 To 5
' 以下のコードを追加
If Me.optDoEvents.Checked Then
Application.DoEvents()
End If
' ここまで
System.Threading.Thread.Sleep(5000)
Me.additem(i.ToString() & "回目の処理完了 " & Now.ToLongTimeString())
Next
End Sub
アプリケーションを実行すると、表示経過がなかなか更新されず、またウィンドウの移動処理も思うようにできないことがわかります。現在の実装では、あくまでも DoEvent を実行した際にのみ、たまったイベント処理が実行されるため、業務処理が重ければあまり効果は期待できないことがわかります。
DoEvents の簡易な処理によってある程度のパフォーマンス改善を行うことができました。しかし、ケースによってはこの処理では不十分な場合もあるでしょう。ですからケースバイケースで対処方法を検討して選択することが必要です。
次は 『非同期処理を使ったパフォーマンスの向上』について説明します。