Cスレッド同期の方法


一、プロセス内部のスレッド同期
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〓スレッド同期に関する資料は他の関連記事に注目してください。