Microsoft Visual C# .NET や Microsoft Visual C# 2005 のアプリケーションでは、マルチスレッドを使用して、同時に複数のタスクを実行できます。マルチスレッドでは、複数のスレッドを開始して、異なるタスクを同時に処理できます。また、マルチスレッドでは、アプリケーションのパフォーマンスと応答が向上します。
複数のスレッドが同時にリソースにアクセスできるため、個々のスレッドをプログラムの他の部分と同期することが必要な場合があります。この資料では、マルチスレッド プログラミングの一般的なシナリオを紹介し、複数のスレッド間で共有リソースへのアクセスを同期する方法について説明します。
マルチスレッド環境でモジュールのグローバル データを保護する
メソッドのパブリック フィールドは、アプリケーションのすべてのスレッドにアクセスできます。パブリック フィールドへのアクセスを同期するには、フィールドの代わりにプロパティを使用し、ReaderWriterLock オブジェクトを使用してアクセスを制御します。これを行うには、次の手順を実行します。
-
Microsoft Visual Studio .NET または Microsoft Visual Studio 2005 を起動します。
-
[ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。
-
[プロジェクトの種類] ボックスの一覧の [Visual C# プロジェクト] をクリックし、[テンプレート] ボックスの一覧の [コンソール アプリケーション] をクリックします。
注 : Visual Studio 2005 では、[プロジェクトの種類] ボックスの一覧の [Visual C# プロジェクト] をクリックし、[テンプレート] ボックスの一覧の [コンソール アプリケーション] をクリックします。
-
[プロジェクト名] ボックスに MultiThreadApplication と入力し、[OK] をクリックします。
-
Class1.cs の既存のコードを次のコードに置き換えます。
注 : Visual Studio 2005 では、デフォルトのファイルは Program.cs です。
using System;
using System.Threading;
namespace MultiThreadApplication
{
class Class1
{
private ReaderWriterLock rwl = new ReaderWriterLock();
private long myNumber;
public long Number // the Number property
{
get
{
//Acquire a read lock on the resource.
rwl.AcquireReaderLock(Timeout.Infinite);
try
{
Console.WriteLine("Thread:{0} starts getting the Number", Thread.CurrentThread.GetHashCode());
Thread.Sleep(50);
Console.WriteLine("Thread:{0} got the Number", Thread.CurrentThread.GetHashCode());
}
finally
{
//Release the lock.
rwl.ReleaseReaderLock();
}
return myNumber;
}
set
{
//Acquire a write lock on the resource.
rwl.AcquireWriterLock(Timeout.Infinite);
try
{
Console.WriteLine("Thread: {0} start writing the Number", Thread.CurrentThread.GetHashCode());
Thread.Sleep(50);
myNumber = value;
Console.WriteLine("Thread: {0} written the Number", Thread.CurrentThread.GetHashCode());
}
finally
{
//Release the lock.
rwl.ReleaseWriterLock();
}
}
}
[STAThread]
static void Main(string[] args)
{
Thread []threadArray = new Thread[20];
int threadNum;
Class1 Myclass = new Class1();
ThreadStart myThreadStart = new ThreadStart(Myclass.AccessGlobalResource);
//Create 20 threads.
for( threadNum = 0; threadNum < 20; threadNum++)
{
threadArray[threadNum] = new Thread(myThreadStart);
}
//Start the threads.
for( threadNum = 0; threadNum < 20; threadNum++)
{
threadArray[threadNum].Start();
}
//Wait until all the thread spawn out finish.
for( threadNum = 0; threadNum < 20; threadNum++)
threadArray[threadNum].Join();
Console.WriteLine("All operations have completed. Press enter to exit");
Console.ReadLine();
}
public void AccessGlobalResource()
{
Random rnd = new Random();
long theNumber;
if (rnd.Next() % 2 != 0)
theNumber = Number;
else
{
theNumber = rnd.Next();
Number = theNumber;
}
}
}
}
-
プロジェクトをビルドし、アプリケーションを実行します。
クラスをスレッド セーフにする
複数のスレッドが 1 つのオブジェクトに同時にアクセスすることがあります。その場合、あるスレッドによってリソースに変更が加えられると、他のスレッドが取得した内容が無効になる場合があります。たとえば、あるスレッドがオブジェクトのフィールドを変更中に、他のスレッドがそのフィールドを読み取ると、後者のスレッドが取得したフィールドの状態が無効な場合があります。この状態を競合状態と呼びます。
この状態の発生を回避するには、ロックを使用して、競合状態からコードのクリティカル セクションを保護します。Visual C# キーワードの lock ステートメントでロックを使用すると、1 つの実行スレッドがオブジェクトの排他的実行の権利を取得できます。以下に、ロックを使用する手順の例を示します。
-
Visual Studio .NET または Visual Studio 2005 を起動します。
-
[ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。
-
[プロジェクトの種類] ボックスの一覧の [Visual C# プロジェクト] をクリックし、[テンプレート] ボックスの一覧の [コンソール アプリケーション] をクリックします。
注 : Visual Studio 2005 では、[プロジェクトの種類] ボックスの一覧の [Visual C# プロジェクト] をクリックし、[テンプレート] ボックスの一覧の [コンソール アプリケーション] をクリックします。
-
[プロジェクト名] ボックスに MultiThreadLockApplication と入力し、[OK] をクリックします。
-
Class1.cs の既存のコードを次のコードに置き換えます。
using System;
using System.Threading;
namespace MultiThreadLockApplication
{
class Student
{
private static string myTeacherName = "Bill";
private string myName = "Grace";
private static object somePrivateStaticObject = new Object();
public static string TeacherName
{
get
{
string theName;
// Synchronize access to the shared member.
lock(somePrivateStaticObject)
{
Console.WriteLine("Thread {0} starts to get the teacher's name",Thread.CurrentThread.GetHashCode());
theName = myTeacherName;
// Wait for 0.3 second.
Thread.Sleep(300);
Console.WriteLine("Thread {0} finished to get the teacher's name:{1}.", Thread.CurrentThread.GetHashCode(), theName);
}
return theName;
}
set
{
lock(somePrivateStaticObject)
{
Console.WriteLine("Thread {0} starts to set the teacher's name.", Thread.CurrentThread.GetHashCode());
myTeacherName = value;
// Wait for 0.3 second.
Thread.Sleep(300);
Console.WriteLine("Thread {0} finished to set the teacher's name:{1}.", Thread.CurrentThread.GetHashCode(), value);
}
}
}
public string GetName()
{
string theName;
lock(this)
{
Console.WriteLine("Thread {0} starts to get the student's name.", Thread.CurrentThread.GetHashCode());
theName = myName;
// Wait for 0.3 second.
Thread.Sleep(300);
Console.WriteLine("Thread {0} finished to get the student's name:{1}", Thread.CurrentThread.GetHashCode(), theName);
return theName;
}
}
public string SetName(string NewName)
{
string theOldName;
lock(this)
{
Console.WriteLine("Thread {0} starts to set the student's name.", Thread.CurrentThread.GetHashCode());
theOldName = myName;
myName = NewName;
// Wait for 0.3 second.
Thread.Sleep(300);
Console.WriteLine("Thread {0} finished to set the student's name:{1}", Thread.CurrentThread.GetHashCode(), NewName);
}
return theOldName;
}
}
class Class1
{
public static int WorkItemNum = 20;
public static AutoResetEvent Done = new AutoResetEvent(false);
public static void AccessClassResource(object state)
{
Random rnd = new Random();
string theName;
Student AStudent = (Student) state;
if( (rnd.Next() %2) != 0)
{
if( (rnd.Next() %2) != 0)
{
switch (rnd.Next() %3 )
{
case 0:
Student.TeacherName = "Tom";
break;
case 1:
Student.TeacherName = "Mike";
break;
case 2:
Student.TeacherName = "John";
break;
}
}
else
{
theName = Student.TeacherName;
}
}
else
{
if( (rnd.Next() %2) != 0)
{
switch (rnd.Next() %3 )
{
case 0:
AStudent.SetName("Janet");
break;
case 1:
AStudent.SetName("David");
break;
case 2:
AStudent.SetName("Ben");
break;
}
}
else
{
theName = AStudent.GetName();
}
}
if(Interlocked.Decrement( ref WorkItemNum) == 0)
{
Done.Set();
}
}
[STAThread]
static void Main(string[] args)
{
int threadNum;
Student AStudent = new Student();
// Queue up 20 work items in the ThreadPool.
for (threadNum = 0 ; threadNum <= WorkItemNum -1 ; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(AccessClassResource),AStudent);
}
Done.WaitOne();
Console.WriteLine("All operations have completed. Press enter to exit");
Console.ReadLine();
}
}
} -
プロジェクトをビルドし、アプリケーションを実行します。
詳細については、以下の MSDN (Microsoft Developer Network) Web サイトを参照してください。
文書番号: 816161 - 最終更新日: 2006年10月4日 - リビジョン: 2.2
この資料は以下の製品について記述したものです。
- Microsoft Visual C# 2005
- Microsoft Visual C# .NET 2003 Standard Edition
- Microsoft Visual C# .NET 2002 Standard Edition
| kbthreadsync kbthread kbhowtomaster KB816161 |
"Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。(Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。"