マルチスレッド入門 | |
|
マルチスレッドとは何か、セカンドスレッドからメインスレッドを操作する
マルチスレッドとは並列処理です。
CLR(Common Language Runtime)は簡単にマルチスレッドを実現するクラスを持っています。 Streamクラスなどはマルチスレッド使用して、ファイルの書き込みや読み出しを効率よく行ないます。 殆どの場合は明示的に複数のスレッドを管理する必要は有りませんが、一部のクラスはマルチスレッド の管理が不可欠となります。 例えばストップ・ウオッチをマルチスレッドで作成して、カウントはセカンドスレッドで、表示をメインスレッドで 行なう場合は、スレッドを意識したコーディングが必要になります。 またRS−232CのComイベントはセカンドスレッドで発生します、この為セカンドスレッドで取得した データをメインスレッドで表示したい場合や、ピンチェンジをメインスレッドで表示したい場合は、 マルチスレッド理解する必要が有ります。 ここでは最初に直面するマルチスレッドの問題点に焦点を絞って説明します。 (以下のマルチスレッドの説明にはデリゲートの理解が必要です、デリゲートについては 「デリゲート入門」 をお読み下さい。) 試しに、簡単なマルチスレッドのコードを作成してみましょう。 先ずフォームにラベルを一つ貼り付けます。 続いてボタンを2つ貼り付けます。 1番目のボタンを押すと、ラベルに現在の時間が表示されます。 2番目のボタンを押すと、第2スレッドを作成して第2スレッドからラベルに時間を表示します。 2番目のボタンクリックイベントの中で第2スレッドを作成するコードは以下のようになります。 C#のコード
Thread t1 = new Thread(new ThreadStart(setTime )); VB.NETのコード
Dim t1 As Thread = New Thread(New ThreadStart(AddressOf SetTime))
このコードはスレッドクラスのインスタンスを作成しています。
nwe Tread(New Tread).....はコンストラクタです。 コンストラクタの引数 C#のコード
new ThreadStart(setTime );
VB.NETのコード
New ThreadStart(AddressOf SetTime)
これは何でしょうか?
実はこれはdelegateです、Threadコンストラクタはdelegate型の引数と一つ取ります。 この引数にはCLR(Common Language Runtime)にあるThreadStartデリゲートを使用しています。 ちなみにThreadStartデリゲートはCLRの中で C#のコード
public delegate void TreadStart(); VB.NETのコード
Public Delegate Sub TreadStart()
と定義されています。
TreadStartデリゲートに登録されるメソドはスレッドで実行したいsetTime(SetTime)メソドです。 さてスレッドのインスタンスを作成しただけでは、スレッドはスタートしません。 スレッドの実行をスタートさせる為にはTreadクラスのStartメソドを呼び出します。 C#のコード
t1.Start(); VB.NETのコード
t1.Start()
これで第2スレッドがスタートしました。
問題はこれからです。 第2スレッドの中からデリゲートで呼び出されるメソドのsetTimeの中に DisplayTime()というメソド しか無かったとします、このメソドはlabel1(Label1)のtext(Text)に文字列を設定するものです。 この場合以下のエラーが表示されます。 マルチスレッドの場合、メインスレッド以外のスレッドからメインスレッドにあるコントロールにアクセス しようとするとアクセスが拒否されるのです。 ではマルチスレッドの場合メインスレッド以外からメインスレッドのコントロールにアクセスできないのでしょうか? 答えは「出来る」です、 C#のコード
Invoke(new MyDelegate(DisplayTime));
VB.NETのコード
Invoke(New MyDelegate(AddressOf DisplayTime))
メイン以外のスレッドからメインスレッドのコントロールにアクセスする場合は、Invokeを使用します。
このInvokeはControl.Invokeと言われるもので、デリゲートを引数に取り、引数のデリゲートの実行を コントロールの有るメインのスレッドで行なうものです。 InvokeはFormが持つメソドであり、次の様に書き換えられます。 C#のコード
this.Invoke(new MyDelegate(DisplayTime)); VB.NETのコード
Me.Invoke(New MyDelegate(AddressOf DisplayTime))
又、label(Label)やbutton(Button)と言ったコントロールもFormから派生したものですので、当然
Invokeメソドを継承しています。
従って、 C#のコード
label1.Invoke(new MyDelegate(DisplayTime));
VB.NETのコード
Label1.Invoke(New MyDelegate(AddressOf DisplayTime))
や
C#のコード
button1.Invoke(new MyDelegate(DisplayTime));
VB.NETのコード
Button1.Invoke(New MyDelegate(AddressOf DisplayTime))
も可能で実行結果は同じになります。
さてsetTime(SetTime)メソドはボタン1を押した時にも呼び出されます。 ボタン1を押した時は通常のメインスレッドからの呼び出しとなり、Invokeメソドを 使う必要は有りません、そこでInvokeメソドが必要か否かを判断する、 InvokeRequiredと言うメソドがあり、Invokeが必要なときはtrue(True)を返します。 C#のコード
if (InvokeRequired)
VB.NETのコード
If InvokeRequired Then
このコードはInvokeが必要か否か、すなわちメソドがメインスレッドから呼び出されたのか、
又はメインスレッド以外から呼び出されたかを判断して処理を行ないます。
どうでしょう、初めての方は難しかったかも知れません、以下まとめて置きます。
サンプルコード
C#、VB.NETともに、フォームに label1(Label1)とbutton1(Button1)を貼り付ける。
C#のコード
using System; VB.NETのコード
Option Strict On |