┏第19号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ■.NET Tips ・.NETのマルチスレッドプログラミング - 新しいスレッドを作成し、実行する - フォアグラウンドスレッドとバックグラウンドスレッドの違い - スレッドに優先順位をつける 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●.NETのマルチスレッドプログラミング .NETのマルチスレッドプログラミングについて、今号から数回にわた って説明していきます。 ★新しいスレッドを作成し、実行する まずは、マルチスレッドプログラミングのはじめの一歩である、新し いスレッドを作成し、実行する方法を紹介します。 マルチスレッドを使用することにより、より効率的なアプリケーショ ンを作成できるケースが数多くあります。例えば、長く時間のかかる 処理の間ユーザーインターフェイスを有効にし、ユーザーの入力に瞬 時に対応する(処理中のキャンセルボタンへの対応など)ことが出来 ますし、複数のURLから同時にファイルをダウンロードするようなアプ リケーションを作成することも出来ます。 (スレッドとは何か、マルチスレッドの利点と欠点などに関して詳し くは、ヘルプの「スレッドおよびスレッド処理」や「マルチスレッド アプリケーション」等をご覧ください。) ・スレッドおよびスレッド処理 http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconthreadsthreading.asp ・マルチスレッド アプリケーション http://www.microsoft.com/japan/msdn/library/ja/vbcn7/html/vaconFreeThreading.asp マルチスレッドはうまく使えば非常に便利ですが、マルチスレッドア プリケーションの作成は非常に難しく、正しく理解せずに使用するこ とは無謀であり、絶対にやめるべきです。残念ながらこの号で私が紹 介する事柄は、マルチスレッドを理解するために必要な知識の半分に も及びませんので、マルチスレッドアプリケーションをはじめて作る という方は更なる勉強が必要になると思ってください。(このメール マガジンでおいおい紹介していくつもりです。) .NET FrameworkのSystem.Threading名前空間にはマルチスレッドをサ ポートするいくつかのクラスが用意されています。まずは、その内最 も基本的なThreadクラスによる新しいスレッドの作成と、開始の方法 について説明します。 まずは、マルチスレッドを用いない次のようなコンソールアプリを見 てみましょう。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ Class MainClass Public Shared Sub Main() Console.WriteLine("スタート") 'SpendLongTimeメソッドを実行 SpendLongTime() Console.WriteLine("待機中...") Console.ReadLine() End Sub Private Shared Sub SpendLongTime() '長い時間のかかる処理があるものとする System.Threading.Thread.Sleep(10000) '処理が終わったことを知らせる Console.WriteLine("終わりました") End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ class MainClass { public static void Main() { Console.WriteLine("スタート"); //SpendLongTimeメソッドを実行 SpendLongTime(); Console.WriteLine("待機中..."); Console.ReadLine(); } private static void SpendLongTime() { //長い時間のかかる処理があるものとする System.Threading.Thread.Sleep(10000); //処理が終わったことを知らせる Console.WriteLine("終わりました"); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ このアプリはまず「スタート」と出力した後、SpendLongTimeメソッド を実行し、SpendLongTimeメソッドがすべて処理されてから、「待機中...」 と表示されます(つまり出力の順番としては、「スタート」、(10秒 待機)、「終わりました」、「待機中...」となります)。 さて、いよいよマルチスレッドの例として、上記コードの SpendLongTimeメソッドを別のスレッドで実行するようにしてみます。 まず、SpendLongTimeメソッドを参照するThreadStartデリゲートオブ ジェクトを作成し、これを使ってThreadオブジェクトを作成します。 そして、スレッドの実行を開始させるために、Thread.Startメソッド を呼び出します。ThreadStartデリゲートには引数も戻り値もありませ んので、引数、戻り値のあるメソッドでは不可です。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ Class MainClass Public Shared Sub Main() Console.WriteLine("スタート") 'SpendLongTimeメソッドを実行するための 'Threadオブジェクトを作成する Dim t As New System.Threading.Thread( _ New System.Threading.ThreadStart( _ AddressOf SpendLongTime)) 'スレッドを開始する t.Start() Console.WriteLine("待機中...") Console.ReadLine() End Sub '別スレッドで実行するメソッド Private Shared Sub SpendLongTime() '長い時間のかかる処理があるものとする System.Threading.Thread.Sleep(10000) '処理が終わったことを知らせる Console.WriteLine("終わりました") End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ class MainClass { public static void Main() { Console.WriteLine("スタート"); //SpendLongTimeメソッドを実行するための //Threadオブジェクトを作成する System.Threading.Thread t = new System.Threading.Thread( new System.Threading.ThreadStart(SpendLongTime)); //スレッドを開始する t.Start(); Console.WriteLine("待機中..."); Console.ReadLine(); } //別スレッドで実行するメソッド private static void SpendLongTime() { //長い時間のかかる処理があるものとする System.Threading.Thread.Sleep(10000); //処理が終わったことを知らせる Console.WriteLine("終わりました"); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ このアプリを実行すると、まず「スタート」と出力され、その後すぐ に「待機中...」と表示し、10秒後に「終わりました」と表示されます。 ここでは先のマルチスレッドを用いない例と違い、SpendLongTimeが別 スレッドで開始されたため、メインスレッドがSpendLongTimeの処理で ブロックされることなく、この2つのスレッドは平行して同時に(シン グルプロセッサでは本当はそうではないが)実行されます。 ★フォアグラウンドスレッドとバックグラウンドスレッドの違い スレッドには、フォアグラウンドスレッドとバックグランドスレッド の2種類があります。プロセス内のフォアグラウンドスレッドがすべて 終了した時にそのプロセスは終了しますが(逆に言えば、すべてのフ ォアグラウンドスレッドが終了しなければプロセスは終了しない)、 プロセス終了時に実行中のすべてのバックグランドスレッドは終了さ せられます(Abortが呼び出されます)。 Threadオブジェクトが新しく作成、起動されたとき、そのスレッドは デフォルトでフォアグラウンドスレッドとなります。また、スレッド プールが使用するスレッドや、アンマネージコードからマネージ実行 環境に入るスレッドは、バックグラウンドスレッドとなります。 スレッドをフォアグラウンドスレッドまたはバックグラウンドスレッ ドにするには、Thread.IsBackgroundプロパティをfalseまたはtrueに します。 具体的な例を見てみましょう。まずはフォアグラウンドスレッドの例 です。MainメソッドはMyMethod1メソッドを別スレッドで実行し、エン ターキーが押されるまで待機します。MyMethod1メソッドはフォアグラ ウンドスレッドで実行しているため、MyMethod1メソッドが終了しなけ れば、エンターキーが押されてもプロセスは終了しません。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする Class MainClass 'エントリポイント Public Shared Sub Main() 'スレッドを作成し、開始する Dim t1 As New Thread(New ThreadStart(AddressOf MyMethod1)) 'フォアグラウンドスレッドとする(デフォルト) t1.IsBackground = False t1.Start() 'エンターキーが押されるまで待機する Console.ReadLine() Console.WriteLine("メインスレッド終了") End Sub Public Shared Sub MyMethod1() Console.WriteLine("スレッド開始") '10秒待機する Thread.Sleep(10000) Console.WriteLine("スレッド終了") End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする class MainClass { //エントリポイント public static void Main() { //スレッドを作成し、開始する Thread t1 = new Thread(new ThreadStart(MyMethod1)); //フォアグラウンドスレッドとする(デフォルト) t1.IsBackground = false; t1.Start(); //エンターキーが押されるまで待機する Console.ReadLine(); Console.WriteLine("メインスレッド終了"); } public static void MyMethod1() { Console.WriteLine("スレッド開始"); //10秒待機する Thread.Sleep(10000); Console.WriteLine("スレッド終了"); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 次はバックグラウンドスレッドの例です。作成したスレッドがバック グラウンドスレッドである点を除いて前のコードと変わりありません。 今度はMyMethod1メソッドをバックグラウンドスレッドで実行している ため、MyMethod1メソッドが終了する前にエンターキーを押してもすぐ にプロセスが終了します。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする Class MainClass 'エントリポイント Public Shared Sub Main() 'スレッドを作成し、開始する Dim t1 As New Thread(New ThreadStart(AddressOf MyMethod1)) 'バックグラウンドスレッドとする t1.IsBackground = True t1.Start() 'エンターキーが押されるまで待機する Console.ReadLine() Console.WriteLine("メインスレッド終了") End Sub Public Shared Sub MyMethod1() Console.WriteLine("スレッド開始") '10秒待機する Thread.Sleep(10000) Console.WriteLine("スレッド終了") End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする class MainClass { //エントリポイント public static void Main() { //スレッドを作成し、開始する Thread t1 = new Thread(new ThreadStart(MyMethod1)); //バックグラウンドスレッドとする t1.IsBackground = false; t1.Start(); //エンターキーが押されるまで待機する Console.ReadLine(); Console.WriteLine("メインスレッド終了"); } public static void MyMethod1() { Console.WriteLine("スレッド開始"); //10秒待機する Thread.Sleep(10000); Console.WriteLine("スレッド終了"); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ★スレッドに優先順位をつける スレッドの優先順位はThread.Priorityプロパティによって変更するこ とが出来ます。優先順位はThreadPriority列挙体で指定し、優勢順位 が高い方から、Highest, AboveNormal, Normal, BelowNormal, Lowestの5段階があります。優先順位はすべてのスレッドにつけられて おり、Threadクラスで作成されたスレッドのデフォルトはNormalとな ります。 この優先順位というのは、OSによるスレッドのスケジューリングの優 先順位のことです。よってその動作はOSによるということになりそう ですが、一般的には優先順位のより高いスレッドの処理にプロセッサ がより多くの時間を割く(より多くのプロセッサタイムスライスを割 り当てる)ことになります。その結果、優先順位の低いスレッドはよ り高いスレッドがすべて終了する(あるいは待機状態になる)まで実 行されないということにもなります。(詳しくはヘルプ「スレッドの スケジューリング」をご覧ください。) ・スレッドのスケジューリング http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconschedulingthreads.asp このスレッドの優先順位を利用することにより、例えば、長く時間の かかる処理の間ユーザーからの入力を待っているような場合、ユーザー からの入力待ちのためのスレッドの優先順位を高くして、その反応を 良くするといったことができます。 優先順位の違いにより、スレッドがどのように実行されるか具体例で 見てみましょう。まずは優先順位をつけずに(Normalのまま)2つのス レッドを作成し、実行してみます。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'エントリポイント Public Shared Sub Main() '2つのThreadオブジェクトを作成する Dim t1 As New Thread(New ThreadStart(AddressOf MyThread)) t1.Name = "1" Dim t2 As New Thread(New ThreadStart(AddressOf MyThread)) t2.Name = "2" '1つのスレッドを開始 t1.Start() 'しばらく待機の後2つ目のスレッドを開始 Dim i As Integer For i = 0 To 99 Console.Write("") Next i t2.Start() '2つのスレッドが終了するまで待つ t1.Join() t2.Join() End Sub Private Shared Sub MyThread() Dim i As Integer For i = 0 To 199 'スレッド名を出力する Console.Write(Thread.CurrentThread.Name) Next i End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //エントリポイント public static void Main() { //2つのThreadオブジェクトを作成する Thread t1 = new Thread(new ThreadStart(MyThread)); t1.Name = "1"; Thread t2 = new Thread(new ThreadStart(MyThread)); t2.Name = "2"; //1つのスレッドを開始 t1.Start(); //しばらく待機の後2つ目のスレッドを開始 for (int i = 0; i < 100; i++) Console.Write(""); t2.Start(); //2つのスレッドが終了するまで待つ t1.Join(); t2.Join(); } private static void MyThread() { for (int i = 0; i < 200; i++) { //スレッド名を出力する Console.Write(Thread.CurrentThread.Name); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 結果は例えば次のようになります(スレッド1が"1"を、スレッド2が "2"を出力する)。2つのスレッドはバラバラに実行されます。(もう 少しループを長くすると分かりやすくなります。) 111111111111111111111111111111111111111111111111111111111111111111111 111111111111222222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222222222222222222222222222222222111 111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111122222222 次に、2番目に実行するスレッドに最高の優先順位であるHighestを指 定して、同様に実行してみます。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'エントリポイント Public Shared Sub Main() '2つのThreadオブジェクトを作成する Dim t1 As New Thread(New ThreadStart(AddressOf MyThread)) t1.Name = "1" Dim t2 As New Thread(New ThreadStart(AddressOf MyThread)) t2.Name = "2" '2つ目のスレッドの優先順位を最高にする t2.Priority = ThreadPriority.Highest '1つのスレッドを開始 t1.Start() 'しばらく待機の後2つ目のスレッドを開始 Dim i As Integer For i = 0 To 99 Console.Write("") Next i t2.Start() '2つのスレッドが終了するまで待つ t1.Join() t2.Join() End Sub Private Shared Sub MyThread() Dim i As Integer For i = 0 To 199 'スレッド名を出力する Console.Write(Thread.CurrentThread.Name) Next i End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //エントリポイント public static void Main() { //2つのThreadオブジェクトを作成する Thread t1 = new Thread(new ThreadStart(MyThread)); t1.Name = "1"; Thread t2 = new Thread(new ThreadStart(MyThread)); t2.Name = "2"; //2つ目のスレッドの優先順位を最高にする t2.Priority = ThreadPriority.Highest; //1つのスレッドを開始 t1.Start(); //しばらく待機の後2つ目のスレッドを開始 for (int i = 0; i < 100; i++) Console.Write(""); t2.Start(); //2つのスレッドが終了するまで待つ t1.Join(); t2.Join(); } private static void MyThread() { for (int i = 0; i < 200; i++) { //スレッド名を出力する Console.Write(Thread.CurrentThread.Name); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 結果は例えば次のようになります。 111111111111111111111111111111111111111111111111111111111111111122222 222222222222222222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222222222222222222222222111111111111 111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111 はじめに優先順位がNormalのスレッド"1"が開始されますが、より高い 優先順位(Highest)のスレッド"2"が開始されるとそちらが優先され、 スレッド"2"が終わってから再びスレッド"1"に実行が移ります。 =============================== ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  http://dobon.net  dobon@bigfoot.com ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 DOBON! All rights reserved. ===============================