Visual Basic 2005 ここが便利!

第4回 Background Workerで夢のマルチスレッドがついに!

株式会社ピーデー 川俣 晶
2005/05/14
Page1 Page2

Visual Studio 2005や.NET Framework 2.0の登場とともに、プログラミング言語であるVisual Basic .NETも「Visual Basic 2005」へとバージョン・アップする。この新しいVisual Basicには、言語仕様の面でも統合開発環境の面でも、プログラミングを楽にする新機能が満載だ。本連載ではその中でも特に注目すべき便利な機能を1つずつピックアップしながら紹介していく。

ああ、夢のマルチスレッド

Back Issue
1
“My”はクラスの海からVBプログラマを救う!?
2 型指定されたコレクションを実現するジェネリック
3 自動生成コードを分離、ソースをすっきりパーシャル・クラス

 太古、パソコンは1度に1つのプログラムしか実行できなかった。例えば、ワープロソフトを使用中に表計算ソフトを使おうと思ったら、ワープロソフトを終了しなければ表計算ソフトを起動することができなかった。また、パソコンは1度に1つの仕事しかこなせなかった。例えば、プリンタへの印刷を開始させると、印刷が終了するまでパソコンは操作を受け付けず、ユーザーはただ待っているしかなかった。つまり利用者にストレスを与えていた。

 このような制約は、パソコンの能力が圧倒的に不足していることによって起こっていた。そして、パソコンの性能向上によって、これらの制約は徐々に解消されていき、いまやワープロソフトと表計算ソフトを同時に開くのは当たり前であるし、プリンタが印刷を開始したからといって操作不能になることもない。利用者のストレスは大幅に軽減された。

 プログラマたるもの、このようなストレスを利用者に感じさせない使い勝手を実現することが使命であり、あこがれであると感じる技術者も決して少数ではないだろう。時間のかかる処理を行ったからといって、操作不能になるような状態はもはや許されない。

 さて、Visual Basicでは、時間のかかる処理を行っているときにもユーザーによる操作を可能とする方法は主に2つある。

 1つは、Visual Basicが生まれたときから用意されている方法で、時間がかかる処理を行っているときには定期的にDoEventsメソッドを呼び出すという方法である。しかし、この方法はあまり使い勝手がよいとはいえない。例えば、何かの理由で呼び出し間隔が開いてしまうと、ユーザーが操作しても反応が返ってこないことになってしまう。それはあまりにも使いにくい。かといって、できるだけ頻繁に呼び出すようにすると、今度はただでさえ時間のかかる処理が、けた違いに遅くなってしまうケースすらあるのである。ちょうどよい間隔でDoEventsメソッドを呼び出すという処理は、実はとてつもなく難しく面倒なのである。

 もう1つの方法は、「マルチスレッド」と呼ばれる機能を使う方法である。これは、Windowsが32bit版に進化した時点(Windows NT 3.1やWindows 95)で利用可能になった機能である。

 マルチスレッドとは、プログラムをスレッドという実行単位に分け、それらが同時並行で実行されているかのように見せかける機能である。実際には、CPUの実行権を細かくスレッド間で切り替えつつ動作するのだが、それはシステムがすべて自動的にやってくれる。スレッドを切り替えるタイミングなどは、プログラマが心配する必要がない。これさえあれば計算処理とユーザーの操作を同時に受け付けるのは簡単である。まさに、期待された夢の機能であった。

VBプログラマの不幸

 さて、OSの機能を最大限に活用できるCやC++のプログラマは、即座にこのマルチスレッド機能を活用したプログラムを作成することができた。しかし、Visual Basicの初の32bit版となるVisual Basic 4.0では、一部のVBプログラマからの熱い希望にもかかわらず、マルチスレッド・プログラミング機能は含まれなかったのである。何という不幸!!

 この状況はVisual Basic 6.0まで続き、その期間は長い。その間、マルチスレッドさえ使えれば! という嘆きは徐々にVBプログラマの間で高まっていたのである。何しろ、C/C++プログラマには使えるものがVBプログラマには使えないのである。こんなに理不尽な差別はない!

 そしてついに来た。Visual Basic .NET(以下、VB.NET)は、待ちに待ったマルチスレッド機能をVBプログラマにもたらしたのだ。待ちに待った日が、やっと来たのだ。

 だが喜びは長く続かなかった。VB.NETでのマルチスレッド・プログラミングは、見慣れないキーワード、回りくどい記述、複雑で厳格なルールなどに満たされていて、とても気軽に使えるようなものではなかったのだ。何という悲劇!

Threadクラスを使ったマルチスレッド

 VB.NETが実現したマルチスレッドとは、基本的に.NET Frameworkクラス・ライブラリのThreadクラス(System.Threading名前空間)を直接利用することで実現する。クラス・ライブラリを直接利用するということは、裏を返せば、統合開発環境の手助けはほとんど期待できないことを意味する。それだけではない。スレッドとは、通常は1本しかない実行の単位(コンテキスト)を複数に増やすという特殊な役割を持つ関係上、普通のプログラミングではあまり見ることのない特殊な機能(例えばデリゲートやAddressOf演算子など)についての知識を要求する。

 実際に、WindowsアプリケーションでThreadクラスを使ったマルチスレッドを実現したサンプル・コードを見てみよう。

 なお、以降のコードはすべてVisual Basic 2005 Express Editionのベータ版で作成している。プログラムの実行には、Windowsアプリケーションのプロジェクトを新規作成し、フォーム上にLabelコントロールとButtonコントロールを1つずつ貼り付けた後に以下のコードを入力する。

Imports System.Threading

Public Class Form1
  Private Delegate Sub sampleCallDelegate()

  Private workerThread As Thread

  Private Sub completed()
    Me.Label1.Text = "Done!"
  End Sub

  Private Sub doWork()
    Thread.Sleep(New TimeSpan(0, 0, 3))
    Me.Invoke(New sampleCallDelegate(AddressOf completed))
  End Sub

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    workerThread = New Thread(AddressOf doWork)
    workerThread.Start()
  End Sub
End Class
Threadクラスを利用したマルチスレッド・アプリケーションの例
時間のかかる処理を行うdoWorkメソッドはメイン・スレッドとは別のスレッドで実行される。ただし別スレッドからコントロールを操作する(この場合にはLabelコントロールに「Done!」を表示する)には、Invokeメソッドを利用しなければならない。なお、このコードはVisual Studio .NET 2003でも実行可能。

 このプログラムは、ボタンをクリックすると新しいスレッドを作成する。このスレッドは3秒間何もせず待機(スリープ)してから(本来ならここで何かの処理を行っているはずである)、ラベルに「Done!」の文字を設定する。待機中もフォームに対する操作は可能となっている。ここでの、doWorkメソッドは別スレッドで実行される本体であり、completedメソッドはスレッド処理終了時に呼び出される処理である。

 さあ、ソース・コードを見ると嫌なキーワードがいくつも出てきた。4行目にDelegate、14行目にInvokeとAddressOf。AddressOfは19行目にも出てくる。いずれも普通のVBプログラミングでは見ることがあまりないキーワードである。そして、なぜdoWorkメソッドのほかにcompletedメソッドが必要とされているかも分かりにくい。これらが存在するために、このコードは恐らく平均的なVBプログラマには、とてもとっつきにくいものになっているのではないかと思う。

 さて、ここではどうしても必要な知識だけを説明しておこう。DelegateやAddressOfなども、Visual Studio 2005では、もう知らなくてもマルチスレッドの世界に行けるというのがこの記事の趣旨であるから解説は行わない。ただ1つ、説明しなければならないのが、なぜdoWorkメソッドのほかにcompletedメソッドが必要とされているかである。

 マルチスレッドで問題になるのは、すべての機能がマルチスレッドで安全に行えるわけではないということである。例えば、フォーム上のコントロールのメソッドなどは一般的に別スレッドから安全に呼び出すことが保証されていない。これを安全に呼び出すために用意されているのが、各コントロールが持つInvokeメソッドである。

 Invokeメソッドを経由してメソッドを呼び出すと、必ずコントロールを作成したメインのスレッドから呼び出しが行われる。つまり、このサンプル・コードの中で、doWorkメソッドはボタンが押されたときに作成されるスレッドで実行されるが、completedメソッドはデフォルトのメイン・スレッドで実行されるという違いがあるということだ。

 ちなみに、completedメソッドをメイン・スレッドで実行する必要があるのは、別スレッドからのアクセスの安全性が保証されないLabelコントロールを操作するためである。一方、doWorkメソッドをメイン・スレッドで実行しないのは、当然のことではあるが、ユーザーからの操作を受け付けるメイン・スレッドで時間がかかる処理を行わないためである。


 INDEX
  Visual Basic 2005 ここが便利!
  第4回 Background Workerで夢のマルチスレッドがついに!
  1.VBプログラマの不幸とThreadクラスを使ったマルチスレッド
    2.さあ、別天地に案内しよう - Visual Basic 2005でマルチスレッド
 
インデックス・ページヘ  「Visual Basic 2005 ここが便利!」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH