Cスレッド同期の方法
一、プロセス内部のスレッド同期
1、ロックを使って、使い方は以下の通りです。
2、使用:InterLocked(原子操作)
System.Threading名前空間の下で、Interlockedは実際にクラスコントロールカウンタであり、プロセスの同期を実現し、生産者消費者モデルを実現しやすいです。
その中でもMonitor.Enterはロックと同じです。
1.Waithandle:
パッケージは、共有リソースを独占的にアクセスするオペレーティングシステムの特定のオブジェクトを待つ。Waithandle:抽象的なタイプです。私たちは普通直接的に使わないで、派生的なタイプを使います。
AutoReetEvent、Event WaitHandle、Manual ReseetEvent、Mutex、Semaphore
この抽象類の方法は以下の通りです。
WaitOne():信号の出現を待って、タイムアウトを設定できます。
WaitAll():複数の信号の出現を待ってタイムアウトを設定できます。
WaitAny():任意の信号の出現を待ってタイムアウトを設定できます。
2、Mutex:Monitorと似ています。一つのスレッドだけがロックを取得できます。WaitOne()でロックを取得し、ReleaseMuttex()でロックを解除します。コンストラクタは次のように使います。
パラメータ2:ロック名は、OpenExist()またはTryOpenExist()を介して既存のロックを開くことができます。オペレーティングシステムは名前のインターロックを識別するので、異なるプロセスで共有できます。ロック名が空の場合は、名前のないインターロックとなり、複数のプロセスの間で共有できません。
パラメータ3: 新しく作成されたインターロックがありますか?
次の例では、Mutexのプロセス間の使用を示します。 クラスプログラム
3.Semaphore:信号量は相互反発ロックに似ているが、一定数のスレッドを同時に使用することができる。次は構造関数です。
パラメータ2:使用可能なロックの数を指定します。
パラメータ3: 信号量の名前はMutexと似ています。
パラメータ4:新規に作成されたインターロックかどうか。
以下の例では、信号量「semaphore 1」を作成し、Palel.For()を用いてFnc 1()を同期して実行し、Fnc 1()では、スレッドが信号量ロックを取得し、ロックを解除したり、タイムアウトを待つと、コンソールに出力されます。
4.AutoReseetEvent類:
イベントを使用して他のタスクに通知することができます。構成関数はpublic AutoReseetEventです。
initial State=trueの場合、signaledモード(終了状態)にあり、waitoneを呼び出しても、タスクをブロックしないで、信号を待って、Resetを呼び出します。方法はnon-signaledモードに設定できます。
initial State=fasleがnon-signaledモードにあるとき、Waitoneを呼び出したら、現在のスレッドがブロックされるのを待っています。set()を呼び出すまで、信号を送ります。
以下の例では、5つのタスクを作成し、それぞれwaitone()ブロッキングスレッドを呼び出し、次に2 sごとにset()を呼び出します。
5.Manual ReseetEvent類:機能は基本的にAutoSetEventと似ていますが、もう一つの違い:
AutoSetEventを使用して、set()を呼び出すたびに、終端モードに切り替わります。ワンウェイワン()をリリースするだけで、自動的に非終了モードに切り替わります。しかし、Manual ReseetEventは、set()を呼び出して、終了モードに切り替えて、現在のすべてのwaitone()を解放して、手動でreset()を呼び出してこそ、非終了モードに切り替えることができます。
この違いを以下の例で説明します。
1、ロックを使って、使い方は以下の通りです。
private static readonly object SeqLock = new object();
private void Print()
{
lock (SeqLock)
{
Console.WriteLine("test");
}
}
特性:転送対象のみで、タイムアウト待ちを設定できません。2、使用:InterLocked(原子操作)
System.Threading名前空間の下で、Interlockedは実際にクラスコントロールカウンタであり、プロセスの同期を実現し、生産者消費者モデルを実現しやすいです。
// ,
private static char buffer;
// ( , 0)
private static long numberOfUsedSpace = 0;
static void Main(string[] args)
{
// :
Thread Writer = new Thread(delegate ()
{
string str = " , ,,,";
for (int i = 0; i < 24; i++)
{
//
// , , Reader
while (Interlocked.Read(ref numberOfUsedSpace) == 1)
{
Thread.Sleep(50);
}
buffer = str[i]; //
// ( 0 1)
Interlocked.Increment(ref numberOfUsedSpace);
}
});
// :
Thread Reader = new Thread(delegate ()
{
for (int i = 0; i < 24; i++)
{
//
// , , Writer
while (Interlocked.Read(ref numberOfUsedSpace) == 0)
{
Thread.Sleep(50);
}
char ch = buffer; //
Console.Write(ch);
Interlocked.Decrement(ref numberOfUsedSpace);
}
});
//
Writer.Start();
Reader.Start();
Console.ReadKey();
3、Monitorを使うその中でもMonitor.Enterはロックと同じです。
Monitor.Enter(obj){
//Synchronized part
}finally{
Monitor.Exit(obj);
}
TryEnterは待ち時間などを設定できます。
bool lockTaken=false;
Monitor.TryEnter(obj, 500, ref lockTaken);
if(lockTaken){
try
{
//Synchronized part
}
finally
{
Monitor.Exit(obj);
}
}else{
//don't aquire the lock, excute other parts
}
二、プロセス間の同期1.Waithandle:
パッケージは、共有リソースを独占的にアクセスするオペレーティングシステムの特定のオブジェクトを待つ。Waithandle:抽象的なタイプです。私たちは普通直接的に使わないで、派生的なタイプを使います。
AutoReetEvent、Event WaitHandle、Manual ReseetEvent、Mutex、Semaphore
この抽象類の方法は以下の通りです。
WaitOne():信号の出現を待って、タイムアウトを設定できます。
WaitAll():複数の信号の出現を待ってタイムアウトを設定できます。
WaitAny():任意の信号の出現を待ってタイムアウトを設定できます。
2、Mutex:Monitorと似ています。一つのスレッドだけがロックを取得できます。WaitOne()でロックを取得し、ReleaseMuttex()でロックを解除します。コンストラクタは次のように使います。
bool isNew = false;
mutex = new Mutex(false, "Mutex1", out isNew);
パラメータ1:ロックが作成された後、メインスレッドが持つかどうか。trueに設定すると、WaitOne()を呼び出したのに相当します。リリースが必要です。そうでないと他のスレッドはロックを取得できません。パラメータ2:ロック名は、OpenExist()またはTryOpenExist()を介して既存のロックを開くことができます。オペレーティングシステムは名前のインターロックを識別するので、異なるプロセスで共有できます。ロック名が空の場合は、名前のないインターロックとなり、複数のプロセスの間で共有できません。
パラメータ3: 新しく作成されたインターロックがありますか?
次の例では、Mutexのプロセス間の使用を示します。 クラスプログラム
private static Mutex mutex = null;
static void Main(string[] args)
{
bool isNew = false;
mutex = new Mutex(false, "Mutex1", out isNew);
Console.WriteLine("Main Start....");
mutex.WaitOne();
Console.WriteLine("Aquire Lock and Running....");
Thread.Sleep(10000);
mutex.ReleaseMutex();
Console.WriteLine("Release Lock....");
Console.WriteLine("Main end....");
Console.ReadLine();
}
}
このコンソールプログラムのexeを2回連続で実行した結果、まずMustex 1を取得してインターロックし、前の動作のリリースMustex 1インターロックを待つという結果になりました。3.Semaphore:信号量は相互反発ロックに似ているが、一定数のスレッドを同時に使用することができる。次は構造関数です。
bool isNew = false;
semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
パラメータ1:作成後、最初にリリースされたロックの数は、パラメータ1が2に設定され、パラメータ2が3に設定されている場合、作成後は2つのロックだけが利用でき、もう1つはロックされています。パラメータ2:使用可能なロックの数を指定します。
パラメータ3: 信号量の名前はMutexと似ています。
パラメータ4:新規に作成されたインターロックかどうか。
以下の例では、信号量「semaphore 1」を作成し、Palel.For()を用いてFnc 1()を同期して実行し、Fnc 1()では、スレッドが信号量ロックを取得し、ロックを解除したり、タイムアウトを待つと、コンソールに出力されます。
class Program
{
private static Semaphore semaphore = null;
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
bool isNew = false;
semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
Parallel.For(0, 6, Func1);
Console.WriteLine("Main end....");
Console.ReadLine();
}
static void Func1(int index)
{
Console.WriteLine("Task {0} Start....",Task.CurrentId);
bool isComplete = false;
while (!isComplete)
{
if (semaphore.WaitOne(1000))
{
try
{
Console.WriteLine("Task {0} aquire lock....", Task.CurrentId);
Thread.Sleep(5000);
}
finally
{
semaphore.Release();
Console.WriteLine("Task {0} release lock....", Task.CurrentId);
isComplete = true;
}
}
else
{
Console.WriteLine("Task {0} timeout....", Task.CurrentId);
}
}
}
運転結果は以下の通りです。スレッド1,2,3はまず信号量ロックを取得します。スレッド4,5,6は1,2,3まで待っています。4.AutoReseetEvent類:
イベントを使用して他のタスクに通知することができます。構成関数はpublic AutoReseetEventです。
initial State=trueの場合、signaledモード(終了状態)にあり、waitoneを呼び出しても、タスクをブロックしないで、信号を待って、Resetを呼び出します。方法はnon-signaledモードに設定できます。
initial State=fasleがnon-signaledモードにあるとき、Waitoneを呼び出したら、現在のスレッドがブロックされるのを待っています。set()を呼び出すまで、信号を送ります。
以下の例では、5つのタスクを作成し、それぞれwaitone()ブロッキングスレッドを呼び出し、次に2 sごとにset()を呼び出します。
private static AutoResetEvent autoReset = new AutoResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("{0} Start....", Task.CurrentId);
autoReset.WaitOne();
Console.WriteLine("{0} Continue....", Task.CurrentId);
});
}
for (int i = 0; i < 5;i++ )
{
Thread.Sleep(2000);
autoReset.Set();
}
Console.WriteLine("Main end....");
Console.ReadLine();
}
運行結果は毎回順序が少し違っています。リリースはランダムです。5.Manual ReseetEvent類:機能は基本的にAutoSetEventと似ていますが、もう一つの違い:
AutoSetEventを使用して、set()を呼び出すたびに、終端モードに切り替わります。ワンウェイワン()をリリースするだけで、自動的に非終了モードに切り替わります。しかし、Manual ReseetEventは、set()を呼び出して、終了モードに切り替えて、現在のすべてのwaitone()を解放して、手動でreset()を呼び出してこそ、非終了モードに切り替えることができます。
この違いを以下の例で説明します。
private static ManualResetEvent manualReset = new ManualResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("{0} Start....", Task.CurrentId);
manualReset.WaitOne();
Console.WriteLine("{0} Continue....", Task.CurrentId);
});
}
Thread.Sleep(2000);
manualReset.Set();
manualReset.WaitOne();
Console.WriteLine("it doesn't work now, Main continue....");
manualReset.Reset();
manualReset.WaitOne();
Console.WriteLine("Main end....");
Console.ReadLine();
}
以上はC〓〓スレッド同期の方法の詳細です。c〓スレッド同期に関する資料は他の関連記事に注目してください。