連載:VB 6ユーザーのための
これならマスターできるVB 2005超入門

第11回 初めてのマルチスレッドと排他制御入門

羽山 博
2007/09/11
Page1 Page2 Page3 Page4

仕事と家庭を両立する方法?!

Back Issue
1
VB 6の皆さん、これはもうVB 2005使うしかないでしょ
2
コントロール配列がなくても大丈夫!
3
VB 2005の“My”の便利さに脱帽!
4
トラブルは水際で防げ〜入力時のチェックとエラー処理
5
データベースはじめの一歩
6
配列ってこんなに便利だったの!?
7
リソースを使ったマルチ・リンガルなVBアプリケーション
8
ファイル入出力をマスターしよう
9
そろそろまじめにクラスに取り組んでみようか
10
今月は部品化月間 〜 ユーザー・コントロールを作成する

 誰でも特技の1つや2つは持っているだろうが、私の特技は、誰かと話をしながらでも、原稿が書けるというものである。といっても、さすがに複雑な会話はできないが、「晩ご飯何食べたい?」「うーん、中華でいいかな」ぐらいの日常会話はキーボードの手を休めずに話すことができる。仕事に集中していても、生返事ではなくきちんと受け答えするという特技は、まあ家庭円満に一役買っているといってもいいだろう。

 というわけで、今回のテーマは「マルチスレッド」だ。スレッドは、「細い糸」だとか「筋道」と訳されるが、その比ゆでは逆にイメージがつかみにくいかもしれない。プログラム全体よりも小さな「一連の処理」と端的にいった方がよさそうだ。マルチタスクの場合は「複数のプログラムが同時に実行されること」というような理解でいいが、マルチスレッドについては、取りあえずは「複数のプロシージャが同時に実行されること」と理解しておくといいだろう。

 マルチスレッドの利点としては、時間のかかる処理を実行しながらでも、ユーザーとの対話がスムーズにできることがまず挙げられるだろう。奥さま……ではなくて、ユーザーをイライラさせないために有効な方法というわけだ。また、多くのユーザーに同じようなサービスを提供する場合にも使われる。

 ただし、プログラムはその分複雑になるし、複数の処理が同時に動くので、処理の順序が重要な場合には、さらにやっかいになることは確かだ(それぞれのスレッドが非同期に実行されるので、何らかの方法で同期を取る必要がある)。裏を返せば、時間のかからない単一の処理をマルチスレッドにしても意味がない、ということになる。

 今回も例によって、できるだけシンプルな例を使って、マルチスレッドを実現する方法を見ていくこととしよう。サンプル・プログラムは現実味には欠けるが、マルチスレッドの機能やその問題点、落とし穴などがよく分かるような例にしてある。まずは「型」を身に付けて、そこから実用・応用に結び付けていってもらえるといいだろう。

サンプル・プログラム16 − 初めてのマルチスレッド・プログラム

 では、できるだけ簡単なマルチスレッド・プログラムを見てみよう。図1に示したのが、そのプログラム。極めて簡単な、いわば単細胞生物レベルの単純な口座管理プログラムである。

図1 初めてのマルチスレッド・プログラム
[開始]ボタンをクリックすれば、スレッドが開始され、当初の残高50000円から20000円を引き落とす。イミディエイト・ウィンドウにスレッドの開始と終了の状況が表示され、スレッドの実行が終了すると、新しい残高が表示される。

 最初、口座には50000円の残高があるものとし、別スレッドを実行して20000円を引き落とすことにする。本来の業務プログラムなら、データベースやファイルに残高が記録されているはずだが、ここでは、話を簡単にするため入出力を省略し、残高が変数に記録されているものとする。

 プログラムを実行し、ウィンドウの中央にある[開始]ボタンをクリックすると、スレッドが開始され、引き落としが行われる。処理の進行状況はイミディエイト・ウィンドウに表示され、結果はフォーム上のラベル・コントロールに表示される。

 では、フォームのデザインから見ていこう。

■フォームのデザイン

 プログラムの実行例を見れば、フォームのデザインもほとんど想像がつくと思うが、一応整理しておこう。新しいプロジェクトを作成し、フォーム上にボタン・コントロールを1つと、ラベル・コントロールを2つ配置する。図2のような感じにすればよい。設定すべき主なプロパティは表1に示したとおりだ。

図2 フォームのデザイン
フォームにボタン・コントロールとラベル・コントロールを配置する。金額を表示するために使うラベル・コントロールは、Textプロパティの文字列を削除しておこう。

番号 コントロール プロパティ 設定値
Form Name frmMTSample
FormBorderStyle FixedDialog
Text スレッドサンプル
Button Name btnStart
Text 開始(&S)
Label Name Label1
Text 残高:
Label Name lblBalance
Text なし
表1 コントロールに設定するプロパティとその値の一覧
赤丸数字は図2に示した各コントロールに対応している。

■コードを記述する

 [開始]ボタンをクリックすると引き落としの処理を実行するので、別スレッドを作成し、それを実行するためのコードはbtnStartボタンのClickイベント・ハンドラに記述すればよい。まず、コードの全体像をざっと眺めておこう。

 ただし、このコードは新しいスレッドを作成して実行するが、まだ、正しい結果は得られない。コードの詳細や、なぜ正しい結果が得られないのかということについては、この後に説明することとする。

Imports System.Threading

Public Class frmMTsample

  Private decBalance As Decimal = 50000D ' 最初の残高
  Private decDeposit As Decimal ' 預入額または引出額
  Private intInterval As Integer ' 処理にかかる時間

  Private Sub mainProc(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click

    Dim myThread1 As Thread
    myThread1 = New Thread(AddressOf WithDraw)

    intInterval = 5000 ' 5秒かかるものとする
    decDeposit = -20000D ' 20000円引き落とすものとする
    myThread1.Start() ' スレッドを開始する

    ' 新しい残高を表示する(うまくいかない例)
    lblBalance.Text = decBalance.ToString
  End Sub

  Private Sub WithDraw()

    Dim decWork As Decimal

    Debug.Print("スレッド開始")
    decWork = decBalance ' 残高の読み出し
    decWork = decWork + decDeposit ' 預金または引き出し
    Thread.Sleep(intInterval) ' 処理にかかる時間
    decBalance = decWork ' 残高の書き出し
    Debug.Print("スレッド終了")
  End Sub
End Class
新しいスレッドを作成し、口座から引き落としをするコード
前半部分がスレッドを作成して実行するコード、後半部分のWithDrawプロシージャが実際に引き落としをするコード。

 まずは、コードの後半に注目してほしい。別スレッドとして実行したいコードは、Subプロシージャとして用意しておく。ここでは、WithDrawというプロシージャを別スレッドとして実行するものとしよう。

 WithDrawプロシージャの内容は単純そのもの。Debug.Printステートメントを使って「スレッド開始」というメッセージをイミディエイト・ウィンドウに表示した後、残高を表す変数decBalanceの値を作業用の変数decWorkに代入している。これは、実際の業務では、データベースやファイルからレコードを読み出す処理に当たる。

 ここでは、decBalanceやdecWorkがDecimal型の変数であることに注意しよう。浮動小数点数を利用すると誤差が発生する可能性があるので、金額を取り扱う計算ではDecimal型の変数を使う。ただし、Decimal型では丸め誤差は発生しないが、整数型や浮動小数点型の変数よりは処理が遅くなる。このプログラムでは利息の計算などがないので、値が小数になることはないが、利用方法を示すためにInteger型やLong型ではなくDecimal型を使った。なお、定数が、小数のあるDecimal型であることを示すには後ろに「D」を、整数のDecimal型であることを示すには後ろに「@」を付ける。

 続けて、decWorkdに預金額(または引出額)を表す変数decDepositの値を加えている。このプログラムでは、decDepositの値が「-20000」なので、引き出し処理ということになる。

 次のThread.Sleepは、現在のスレッドの実行を一時停止するというメソッド。実際の業務での時間のかかる処理を実行する代わりに、Sleepメソッドを使ってその動作を疑似的に再現しているだけである。Sleepメソッドの引数にはミリ秒単位の時間を指定する。この例では、intIntervalの値が5000なので、5秒かかる処理がここで実行されていると見立てているわけだ。

 時間のかかる処理が終わったら、最後に、decBalanceにdecWorkを代入する。これは、データベースやファイルにレコードを書き出す処理に当たるものだ。すべてが終了したら、Debug.Printステートメントを使って「スレッド終了」というメッセージをイミディエイト・ウィンドウに表示する。


 INDEX
  連載:VB 6ユーザーのための<BR>これならマスターできるVB 2005超入門
  第11回 初めてのマルチスレッドと排他制御入門
  1.サンプル・プログラム16 − 初めてのマルチスレッド・プログラム(1)
    2.サンプル・プログラム16 − 初めてのマルチスレッド・プログラム(2)
    3.サンプル・プログラム17 − Mutexによる排他制御
    4.Mutexを利用した排他制御を組み込む
 
インデックス・ページヘ  「これならマスターできるVB 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