第2回 .NETにおけるマルチスレッドの実装方法を総括:連載.NETマルチスレッド・プログラミング入門(1/4 ページ)
単純なスレッドから、スレッドプールやデリゲートを使った、より機能的なスレッドまで、.NETスレッド・プログラミングを総括。
前回は、マルチスレッド・プログラミングの概念と仕組み、およびマルチスレッドによって得られるメリットについて解説した。今回は、具体的に.NETにおけるマルチスレッド・プログラミングの実装方法を紹介する。
.NETにおけるマルチスレッド・プログラミングの方法
.NETでは、マルチスレッドを実現する方法が複数用意されている。それぞれの方法の特徴を表にまとめてみた。まずはこの表に示した各方法の概要について簡単に触れた後、具体的な実装方法を解説していく。
方法 | メリット | デメリット |
---|---|---|
スレッド (Thread) |
・ 各スレッドに優先順位を設定できる ・ スレッドの一時停止/再開/中断を行うことができる |
・ スレッドの作成と破棄を繰り返すとパフォーマンスが落ちる ・ メソッドにパラメータを設定できない ・ メソッドの戻り値を得るのが困難 |
スレッドプール (ThreadPool) |
・ 効率よく複数のスレッドを実行できる ・ object型のパラメータを1つだけ設定できる |
・ パラメータがobject型1つのみ ・ メソッドの戻り値を得るのが困難 ・ 優先順位付けや待機、停止など、スレッドの細かな制御が難しい ・同時に実行できるスレッドの数が制限されている |
デリゲート (BeginInvoke) |
・ メソッドに型のあるパラメータを指定できる ・ 簡単に戻り値を得ることができる |
・ 優先順位付けや待機、停止など、スレッドの細かな制御が難しい ・ 同時に実行できるスレッドの数が制限されている |
タイマー (Timer) |
・一定時間間隔でメソッドを実行することができる | ・スレッドプールがいっぱいだとうまく動作しない |
マルチスレッドの実装方法と特徴一覧 |
●マルチスレッドの基本「スレッド」
.NETマルチスレッド・プログラミングにおいて、最も基本となるのがスレッド(Threadクラス)である。Threadクラス(System.Threading名前空間)は、指定したメソッドを別スレッドで起動(実行)する仕組みを提供する。スレッドの一時停止や中断、優先順位付けなどを細かく制御することもできる。
ただし、サーバ型のプログラムのように、リクエストを並行して処理するようなプログラムの場合、スレッドの生成・破棄を大量に繰り返す必要が出てくる。スレッドの生成にはそれなりのリソースが消費されるため、単純にこれを繰り返していてはパフォーマンスが低下する。
●スレッドのパフォーマンス問題を解決する「スレッドプール」
この問題を解決する仕組みが「スレッドプール」である。スレッドプールとは、キューに入れられたリクエスト(処理)をスレッドプールが用意しているスレッドにより次々に実行していく仕組みである。
スレッドプールでは、一度確保したスレッドのリソースをできる限り再利用するように設計されている。そのため、別スレッドで実行したいメソッドを、スレッドプール専用のキューに次々と入れる(登録する)だけで、後は自動的に登録したメソッドが別スレッドで効率よく実行されていく。
スレッドプールの仕組み
スレッドプールでは、別スレッドで実行したいメソッドを、スレッドプール専用のリクエスト・キューに次々と登録するだけで、後は自動的に登録したメソッドが別スレッドで効率よく実行されていく。
スレッドプールの機能は、主にThreadPoolクラス(System.Threading名前空間)で提供される。
●パラメータや戻り値が利用可能なマルチスレッド「デリゲート」
「デリゲート」によるマルチスレッドも、内部的には先のスレッドプールを利用している。しかし、デリゲートによるメソッドの呼び出しは、通常のメソッドの呼び出しと同様にパラメータや戻り値を持つことができる。
デリゲートについて簡単に説明しておこう。デリゲートは「メソッドの呼び出しをラッピングしたもの(包んだもの)」と理解してよい。メソッドをデリゲートでラッピングすると、デリゲートに登録したメソッドを、デリゲートを通じて間接的に呼び出すことができる。これは「メソッド呼び出しの仮想化」と呼ばれる。
複数のメソッドを1つのデリゲートに登録して一度に呼び出したり、登録したメソッドを動的に入れ替えたりすることができ、デリゲートはアプリケーションの設計上極めて便利に働くのだが、それについてはここでは詳しくは説明しない。
デリゲートに登録されたメソッドを実行するには、普通に実行するInvokeメソッドのほかに、別スレッドでメソッドを実行するBeginInvokeメソッドが使用できる(これらのメソッドはデリゲート・オブジェクトが持つメソッドである)。BeginInvokeメソッドを使用する場合には、デリゲートは上述したスレッドプールを利用して、登録されたメソッドを実行するという仕組みになっている。
デリゲートの仕組み
デリゲートでは、普通にメソッドを実行するInvokeメソッドだけでなく、別スレッドでメソッドを実行するBeginInvokeメソッドが使用できる。BeginInvokeメソッドは、スレッドプールを利用して、登録されたメソッドを実行する仕組みになっている。
●一定時間ごとに処理を実行する特殊なマルチスレッド「タイマー」
一定期間ごとにスレッドを呼び出して、指定した処理を実行させる特殊なマルチスレッドの実現方法として、「タイマー」(Timerクラス)という方法も用意されている。
.NET Frameworkのクラス・ライブラリには異なる名前空間でいくつかのTimerクラスが存在するが、System.Threading名前空間のTimerクラスがこの機能を提供している。なお、タイマーも内部的にはデリゲートと同様に、スレッドプールの機能を利用して実装されている。
以上、.NETで用意されているマルチスレッド・プログラミングの実現方法のそれぞれの特徴について簡単に説明したが、1点明記しておくべきことがある。
Threadクラス以外のThreadPoolクラス、デリゲートおよびTimerクラスは、スレッドプールを用いてマルチスレッド処理が実装されているが、.NETが提供するスレッドプールには、「同時に実行できるスレッドの数に制限がある」ということに注意しなくてはならない。これは1プロセス当たり「プロセッサの数×25」個のスレッドしか同時に実行できないという制限である。次回でもう少し詳しく解説するが、同じアプリケーション内で制限数以上のスレッドをスレッドプールで実行しようとすると、うまく動作しないという不具合が発生する可能性が出てくるので注意してほしい。
以下、それぞれの方法について具体的な実装方法を、サンプル・コードを交えて説明する。
Copyright© Digital Advantage Corp. All Rights Reserved.