RSS

[.NET] Thread


2005のみ?
http://www.jurapun.com/Tutorial/CSharp/Thread.shtml

.NET Frameworkでは、スレッドを使うときThreadクラスとThreadStartデリゲートを使います。ThreadStartデリゲートは、次のように定義されています。

[ComVisibleAttribute(true)] public delegate void ThreadStart();

Threadクラスのコンストラクタは、4つのバージョン(オーバーロード)がありますが、ThreadStartデリゲートを使うバージョンは、

Thread(ThreadStart)

と宣言されています。デリゲートとは、安全な関数ポインタみたいなものなので、ThreadStartにはスレッドとして動作させるメソッドを設定します。

(注意) ThreadStartデリゲートをインスタンス化せずに
      Thread th = new Thread(Func);
 のように関数名を指定してもかまいません(自動的にデリゲートに変換されます)。

 

ThreadStartデリゲートの作るには
バックグランドで動作するメソッドの名前を引数としてThreadStartをインスタンス化する。
 例 new ThreadStart(DoWork);
バックグランドで動作するメソッドのプロトタイプは、戻り値なし(void型)、引数なしであること。
 例 void DoWork();
 

スレッドを使う手順
ThreadStartデリゲートを引数として、Threadクラスをインスタンス化する。
ThreadクラスのStartメソッドを呼び出す。
結局、次のようなコードを実行するとスレッドが走ります。

Thread thread = new Thread(new ThreadStart(Work));
…………
…………

void Work()
{
….
}

 

サンプル1
 単純なサンプルです。スレッド関数を単に走らせるだけです。

 

ThreadStartデリゲートの問題点
 ThreadStartデリゲートの関数プトロタイプは、

void Function();
という形式なので、パラメータを渡すことができません。これは実用上不便なので普通は、クラスを用意しそのコンストラクタのパラメータとしてスレッドのパラメータを渡します。そして、そのパラメータをThreadStartデリゲートで指定したメソッド(関数)が使用して処理を行うようにします。

サンプル2
 スレッド用クラスを用意して、そのメンバ関数をスレッドとして走らせるサンプル

 

簡単にスレッドを使うためのクラス
 スレッドを使うたびに、クラスを作って同じような処理を行うためのコードを書くのは面倒なので、Java風のRunnableという抽象クラスを作って、それをベースとしてスレッド用クラスを作ると便利です。サンプル3は、Runnableクラスとその使用例です。

サンプル3 (Runnable.cs, ThreadTest3.cs)

 スレッドを便利に使うためのRunnableクラスを使ったサンプル

 

ParameterizedThreadStartデリゲートを使う
 ParameterizedThreadStartデリゲートは、オブジェクトを引数とする関数を設定できます。ということは、データを直接にスレッドに渡すことができます。

ParameterizedThreadStartデリゲートを使うときの要件

スレッド関数は、void型でパラメータはobject型1個であること。
スレッドの開始は、Threadクラスのvoid Start(object)メソッドを使うこと。このメソッドの引数として、パラメータをスレッド関数に引き渡すことができる。
なお、このデリゲートは.NET Framework 2.0以上で使用できます。

サンプル4
 ParameterizedThreadStartデリゲートを利用して、スレッドに直接データを渡して動作させる例。

 

スレッドの終了を待つには
 サンプル1~4までは、スレッドの終了を待たずにMainが終了していました。これでは、すけっどの実行結果の表示やエラーがあったときの処理ができません。スレッドが終わるまで待つには、ThreadクラスのIsAliveプロパティを監視します。監視ループの中では、ThreadクラスのSleepメソッドを使って、メインスレッドをある時間間隔でスリープさせながら監視します。そうしないと、CPUの負荷が高くなって動作が重くなります。

(参考) Joinメソッドを使う方法(下記)も考慮してください。

サンプル5
 サンプル4でスレッドの終了を待つようにしたサンプル。

 

呼び出しもとのスレッドをブロックするには
 呼び出しもとで、スレッドの監視をすると無駄なCPUタイムを消費するし、コードも書かなくてはなりません。と言う訳で、Joinメソッドというものが用意されています。これを使うと、サンプル5はサンプル6のように書き換えできます。

サンプル6
 サンプル5でjoinメソッドを使ってスレッドの終了を待つようにしたサンプル。

 

スレッドどうしの競合を回避するには
 複数のスレッドが競合するリソースを使うときには、競合を回避する必要があります。例えば、あるスレッドがあるデータを書き換えている最中に、別のスレッドがそのデータを読み出すと困ったことが起こるかもしれません。そのような場合には、そのリソースをロックして、他のスレッドがアクセスできないようにすることができます。

lockキーワードは、次のような感じで使います。

void DoWork() {
……
lock (object1) { // object1は他のスレッドと競合するオブジェクト
…… // 他のスレッドと競合するコード
}

サンプル7
 2つのスレッド関数が1つのオブジェクトを使うサンプル。

(参考)
 Monitorクラスを使ってスレッドの競合を回避することもできます。lockキーワードはMonitorクラスを使って実装されているそうです。

 

スレッドどうしで同期を取るには
 あるスレッドから別のスレッドへイベントを通知して、複数のスレッドが協調しながらある動作をさせたいときがあります。そのような場合は、AutoResetEventクラスやManualResetEventクラスを使います。

これらのクラスを使うと、スレッドはシグナルを通じて相互に通信できます。AutoResetEventクラスとManualResetEventクラスの違いは、シグナル状態のリセットが自動で行われるのか、手動で行うかです。ManualResetEventは複数のスレッドにシグナルを送るとき使います。

AutoResetEventクラスの使い方

AutoResetEventクラスをインスタンス化する。AutoResetEventコンストラクタは、bool型パラメータで初期のシグナル状態を指定する。
(例) AutoEventReset event1 = new AutoEventReset(false); // 非シグナル状態で初期化
イベント待ち側のスレッドは、WaitOneメソッドを使って、シグナル待ちで待機する。このとき、そのスレッドはブロッキング状態になる。
イベント発信側のスレッドは、発信可能になったらSetメソッドを使ってイベントをシグナル状態にする。
WaitOneメソッドで待機していたスレッドがブロッキングが解除され処理が続行される。
サンプル8
 あるスレッドをシグナル待ちにして、別のスレッドがシグナルを送るサンプル

 

(参考) プロセス間で同期を取るときは、Mutexクラスを使います。

 

Windowsフォームでのバックグランド処理
Windowsフォームでバックグランド処理をするには、BackgroundWorkerコントロールを使うと簡単です。バックグランド処理は、DoWorkイベントハンドラを追加しその中にバックグランド処理を記述します。バックグランド処理に開始は、RunWorkerAsyncメソッドで開始することができます。

バックグランド処理が終了すると、RunWorkerCompletedイベントが発生するのでその中で後処理を行うことができます。

APIをコールせずに時間がかかる処理を行う場合は、Application.DoEventsメソッドをループ内でコールして、Windowsメッセージポンプを動作させます。そうしないと、Windowsメッセージがリアルタイムで処理されないため、他の操作が阻害されます。

 

Windowsフォームでスレッドを停止させるには
ApplicationクラスのExit()やExitThread()メソッドを使います。

  1. コメントはまだありません。

  1. トラックバックはまだありません。

*