Paken.NET

筑波大学附属駒場中・高等学校 パーソナルコンピュータ研究部
<< 2008年02月 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 >>
<< ~いろいろなクラスを見てみよう~ 第03回 "System.Reflection.Assembly" | トップ |

~いろいろなクラスを見てみよう~ 第04回 "System.Threading.Thread"

2007年10月10日 水曜日 | Paken > isa-inguz |

今回はSystem.Threading.Threadを扱います。

とりあえずこのクラス、何をするクラスかと言いますと、マルチスレッドを実装する上でスレッド一つ一つに対応して管理を行うクラスです。

また、スレッドに関連する幾つかのスタティックメソッドを提供します。

今回は、このクラスの基本的な使い方を見ながら、簡単なマルチスレッドプログラムを作れるようになるまでの方法を解説します。

マルチスレッドが使えると、やはりプラグインが使えるのと同じくらいプログラムの発展性が広がります。

また、単純な発展性だけでなく、各種通信において必要になる非同期メソッドに慣れ親しみやすくなるので様々な技術をプログラムに取り入れる助けになるでしょう。

では、まずはスタティックメソッドから見ていきますが、基本的なマルチスレッドを実装する上で知っておくべきなのはSleepだけです。それ以外は後々見ていけばよいでしょう。

Sleepは void Sleep (int millisecondsTimeout) と定義されています。

このメソッドが行うのは、名前から想像できるように、”現在のスレッドを眠らせること”です。つまりはスレッドのブロックを行う訳です。

ブロックされている間はそのスレッドは進行しません。

引数はブロックを行う時間をミリ秒単位で指定します。

 

さて、ではインスタンス化されたThreadの使い方を見ていきましょう。

とりあえず目的は次の通りです。

新しく一つのスレッドを作成する。

そのスレッド上でメソッドを実行する。

実際にプログラミングする上での順序としては、Threadクラスのインスタンスを生成。この際に実行メソッドを登録 → スレッドの実行 という流れになります。

つまり、コンストラクタと実行させるメソッドの二つだけで動かせる訳です。

 

まずコンストラクタは public Thread (ParameterizedThreadStart start) と定義されています。(.NetFramework2.0以降のみ)

ParameterizedThreadStartというのはマルチスレッドで実行させるメソッドを格納するデリゲートで、 delegate void ParameterizedThreadStart (Object obj) と定義されています。

つまり、これは引数を何でも一つ持て、戻り値の無いデリゲートです。objには何かそのメソッドで必要となるデータを与えましょう。

また、戻り値が無い為、新スレッド上で実行されたメソッドの計算結果などを得るためにはいろいろな工夫をする必要があります。これについては後述。

 

次に、実行に必要なのは void Start (Object parameter) メソッド。(2.0以降のみ)

実に単純なメソッドなので、わざわざ解説しなくてもおそらく推測できることと思いますが、これを実行するとスレッドが新しく立ち、コンストラクタに与えたデリゲートが実行されます。

引数parameterに与えたオブジェクトは先ほどのParameterizedThreadStartに於けるobjに渡され、メソッド上で処理されます。

あと、通常はStart()の前に IsBackground パラメータをtrueに設定しておきましょう。

これがtrueに設定されていると、プロセスの終了を阻害しません。逆に言えば、プロセスが終了するときには強制的に終了されます。

逆にfalseに設定されていると、このスレッドが終了するまではプロセスが終了できません。

用途に応じて使い分けましょう。

 

さて、はい。実はこれだけなんです。マルチスレッドに最低限必要なのは。

滅茶苦茶簡単ですよね。

これだけではあまりにもあれですので、先ほど言いました、新スレッドでの計算結果をメインスレッドで利用するための方法をご解説。

これにはクラスフィールドを用意するか、引数に操作可能なクラスを与えて、お互いにあるリソースを通じてデータを共有します。

これが一番単純で簡単な方法と言えるでしょう。

なお、書き込みの際には双方のアクセスが同時に行われてデータが壊れたりしない様、lockステートメントを追加して排他制御を行うようにしましょう。

 lock(object){ / 処理A / }

とすることでobjectを処理Aの間排他モードで管理できます。

なお、二つ以上のスレッドで互いに共有しているリソースをlockする様な構成になっているとデッドロックが発生する事が有り得るので、十分注意してコーディングしてください。

例えばスレッドAがリソースX→リソースYの順でロック・アクセスを行い、スレッドBがリソースY→リソースXの順でロック・アクセスを行う場合、互いにX、Yを持った段階で次の処理に進めなくなってしまいます。

しかし、lockステートメントは便利ですし簡単にコーディングできて強力ですので、デッドロックをあまり恐れずに是非有効活用しましょう。

他にも多くの非同期プログラムにおけるデータ共有の方法があるのですが、Threadクラスを素で使うだけの、この様な単純なケースではこれだけで十分と思われます。

ただ、この方法ではWindowsコントロールのプロパティにはアクセスできません。

何故ならWindowsコントロールには、そのコントロールが生成されたスレッドからしかメンバを変更できないという制約があるからです。

この問題の解決には IAsyncResult Control.BeginInvoke (Delegate method, params Object[] args) メソッドを使います。

IAsyncResultについてはまだ知っておく必要はあまりありません。

methodには実行したいコードを記述したメソッドのデリゲートを、argsにはそのデリゲートに与える引数を指定します。

デリゲートはBeginInvokeのためだけに宣言するのも面倒ですので、例えばテキストボックスの文字を変更するだけと言ったような単純なメソッドを実行したい場合には、匿名メソッドを使いましょう。

 BeginInvoke(delegate(string s) { TextBox_1.Text = s; }, "Foo!");

と言った感じで使えます。

とりあえずこうすると無駄なデリゲートも増えず、コードもすっきりしますね。

ところでdelegate(string s)とするだけで、戻り値の型を書く必要がないのはやはり型推論によるのでしょうか。残念ながら詳しい事はあまり分かりません。

 

また、新スレッド内メソッドが終了したか否かを通知するような機構も欲しいものですが、これには ManualResetEvent などを使うと良いでしょう。

これは他のスレッドから Set() メソッドが実行され、パラメータがfalseになるまでスレッドをブロックし続けるという機能を持つクラスです。

 WaitOne(int millisecondsTimeout, bool exitContext) でmillisecondsTimeoutミリ秒をタイムアウト期限としてスレッドをブロックします。exitContextは今はfalseにして置いて下さい。

 Set() でブロックされているスレッドを動かします。SetされたあとまたすぐにWaitOneしてもスレッドはブロックされないので注意。

再ブロックするにはReset()を実行してからにしましょう。

 

今回は余り必要では無い気がしますのでサンプルコードはなしで。

では、また今度。

paken | コメント (0) | トラックバック (0)

コメント









 

トラックバック

トラックバックURL :