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

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

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

■Mutexを利用した排他制御を組み込む

 Mutexを利用して排他制御をするには、ほかからの読み出しや書き込みの実行を待ち状態にしたい(ブロックしたい)個所の手前で、MutexクラスのWaitOneメソッドを実行する。その後、必要な処理を実行した後、MutexクラスのReleaseMutexメソッドを実行するとブロックが解除される。メイン・スレッドの方は一切変更しなくてよい。

 以下のように、フォーム・レベルでMutexクラスのオブジェクトを作成し、WithDrawプロシージャを書き換えよう。

Imports System.Threading

Public Class frmMTsample

  Private decBalance As Decimal = 50000D ' 最初の残高
  Private intInterval As Integer ' 処理にかかる時間
  Private mtx As New Mutex

  Private Sub mainProc(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
     
     
  (変更なし)
     
     
  End Sub

  Private Sub WithDraw(ByVal data As Object)

    Dim decWork As Decimal

    Debug.Print(Thread.CurrentThread.Name & "開始")

    mtx.WaitOne()

    decWork = decBalance ' 残高の読み出し
    decWork = decWork + Ctype(data, Decimal) ' 預金または引き出し
    Thread.Sleep(intInterval) ' 処理にかかる時間
    decBalance = decWork ' 残高の書き出し

    mtx.ReleaseMutex()

    Debug.Print(Thread.CurrentThread.Name & "終了")
  End Sub
End Class
Mutexを利用した排他制御のためのコード
  Mutexクラスのオブジェクトを作成する。
  WaitOneメソッドを実行し、ほかのコードの実行をブロックする
  ReleaseMutexメソッドを実行し、ブロックを解除する。

 このようにコードを書き換えて実行すると、正しい結果(60000)が表示される。こちらについても、図で処理の流れを示しておこう。図6がその様子だ。

図6 Mutexを使った排他制御
WaitOneメソッドでコードの実行をブロックし、ReleaseMutexメソッドでブロックを解除する。
  WaitOneメソッドを実行する。ブロックされた状態になる。
  別のスレッドでWaitOneメソッドを実行する。コードがブロックされ、預け入れスレッドは待ち状態になる。
  残高を書き戻した後でReleaseMutexメソッドを実行し、ブロックを解除する。
  ブロックが解除されたので、預け入れスレッドが実行される。
  残高を書き戻した後でReleaseMutexメソッドを実行し、ブロックを解除する。

 WaitOneメソッドを実行したとき、ほかのスレッドでWaitOneメソッドが実行されていなければ、自分自身はコードの実行を続け、ほかのスレッドをブロックする。ただし、ほかのスレッドは、その時点で待ち状態になるわけではないことに注意。ほかのスレッドでは、あくまでも、WaitOneメソッドを実行しようとしたときに待ち状態になる。

 コードの実行を続けているスレッドでは、必要な処理が終わった後に、ReleaseMutexメソッドを実行してMutexを解放する。するとブロックが解除され、待ち状態にあったスレッドが実行を開始できるというわけだ。

 イメージがわきにくいようであれば、コードの「利用権」が1つあって、WaitOneメソッドはその利用権をもらうメソッド、ReleaseMutexメソッドはその利用権を放棄するメソッドだと考えればよい。

 誰も利用権を使っていない状態であれば、WaitOneメソッドを実行すれば利用権が得られ、コードが実行できる。これが図の に当たる。ところが、ほかの人が利用権をすでに持っているときに、WaitOneメソッドを実行しても、使える利用権がないので、待っているしかない。これが に当たる。必要な処理が終わってReleaseMutexメソッドを実行し、利用権を放棄するのが だ。 では、ようやく利用権が得られたので、次のコードが実行できるようになる。

 なお、この図6では、たまたま引き落としスレッドが先に実行を開始しているが、預け入れスレッドが先に実行を開始したとしてもうまくいく。

結び − いくつか補足

 今回は、複数のスレッドを使って同時に処理を実行する方法を見てきた。その中で、Mutexを1つ使って排他制御をする方法も見た。Mutexには名前を付けることにより、アプリケーション間でも排他制御を行うことができる。

 排他制御は、ほかにもSyncLock〜End SyncLockステートメントやMonitorクラス、Semaphore(セマフォ)と呼ばれるクラスなどを利用して実現することもできる。Semaphoreを利用すると、今回見たような簡単な排他制御だけでなく、コードを実行できるスレッドの個数を制限するといったこともできる。また、複数のMutexを使えば同期制御もできる。同期制御の場合、複数のスレッドで、すべてのコードが特定の位置まで実行されるのを待ち、足並みをそろえて次の処理に移ることができる。スレッドについてより深く学習したいなら「連載:.NETマルチスレッド・プログラミング入門(全4回)」をご覧いただけばよいだろう。

 なお、同じデータを複数のプログラムで利用する場合には、デッドロックにも注意が必要だ。例えば、次のような処理ではデッドロックが起こり、どちらのスレッドも身動きが取れなくなってしまう。

  1. スレッド1がAというデータを利用し、ロックする。
  2. スレッド2がBというデータを利用し、ロックする。
  3. スレッド1がBを利用しようとする。しかし、ロックされているので待ち状態になる。
  4. スレッド2がAを利用しようとする。しかし、スレッド1が待ち状態なので、ロックが解除されず、スレッド2も待ち状態になる

 今回はスレッドやMutexの働きが理解しやすいように、分かりやすさを優先させ、口座管理という身近な例を使った。しかし、そのような業務ではデータベースを利用するのが普通。データベース管理システムには排他制御の仕組みが備わっているので、そちらを活用すべきである。今回の例はあくまでも、スレッドの排他制御の方法を示したものであるということをお忘れなきよう。End of Article


 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