ccaを深く理解して、マルチスレッドプログラミング


一、スレッドを使う理由
1、スレッドを使ってコードを他のコードから分離し、アプリケーションの信頼性を高めることができます。
2、スレッドを使って符号化を簡略化することができる。
3、スレッドを使って、同時実行が可能です。
二、基本知識
1、プロセスとスレッド:プロセスはオペレーティングシステムの実行プログラムの基本単位として、アプリケーションのリソースを持ち、プロセスはスレッドを含み、プロセスのリソースはスレッドに共有され、スレッドはリソースを持たない。
2、フロントスレッドとバックグラウンドスレッド:Thread類の新規スレッドを通じて、デフォルトはフロントスレッドです。すべてのフロントスレッドがオフになると、すべてのバックグラウンドスレッドが直接終了し、異常が発生しません。
3、吊り上げ(Suspend)と起動(Resume):スレッドの実行順序とプログラムの実行状況は予知できないので、吊り上げと目覚ましを使うとデッドロックが発生しやすい場合、実際のアプリケーションではなるべく少なく使うべきです。
4、スレッドをブロックする:Join、スレッドが終了するまで停止します。
5、終了スレッド:Abort:ThreadAbortExceptionを投げて異常にスレッドを停止させ、終了後のスレッドは起動できない。Interrupt:ThreadInterrupt Exceptionをスローし、異常をキャッチすることで、スレッドを終了させます。
6、スレッド優先度:AboveNormal BelowNormal Highest Lowest Normal、デフォルトはNormalです。
三、スレッドの使用
スレッド関数は、転送を委託することにより、パラメータを持たなくてもいいし、パラメータを持ってもいいです。

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      Thread t1 = new Thread(new ThreadStart(TestMethod));
      Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
      t1.IsBackground = true;
      t2.IsBackground = true;
      t1.Start();
      t2.Start("hello");
      Console.ReadKey();
    }

    public static void TestMethod()
    {
      Console.WriteLine("         ");
    }

    public static void TestMethod(object data)
    {
      string datastr = data as string;
      Console.WriteLine("        ,   :{0}", datastr);
    }
  } 
}
四、スレッド池
スレッドの作成と廃棄には一定の費用がかかりますので、スレッドを使いすぎるとメモリ資源が無駄になり、性能を考慮してスレッド池の概念を導入しました。スレッドプールは要求キューを維持し、スレッドプールのコードをキューから抽出し、スレッドプールのスレッド実行を依頼します。スレッド実行が完了したらすぐに破壊されません。これにより、バックグラウンドでタスクを実行することができます。スレッド作成と廃棄によってもたらされた販売量を低減することができます。
スレッドスレッドはデフォルトではバックグラウンドスレッドです。

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      //             ,            
      ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
      Console.ReadKey();
    }

    public static void TestMethod(object data)
    {
      string datastr = data as string;
      Console.WriteLine(datastr);
    }
  }
}
五、Task類
ThreadPoolを使用したQueueueueUserWorkItem()の方法は、非同期スレッドの実行を開始するのは簡単ですが、この方法の最大の問題は、内部で構築されたメカニズムがないということです。操作がいつ完了するかを教えてくれます。そのためには、System.Threading.Taskの中のTaskクラスを使用することができる。
Task<TResult>オブジェクトを作成し、動作の戻りのタイプを汎型TResultパラメータに伝達します。

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
      t.Start();
      t.Wait();
      Console.WriteLine(t.Result);
      Console.ReadKey();
    }

    private static Int32 Sum(Int32 n)
    {
      Int32 sum = 0;
      for (; n > 0; --n)
        checked{ sum += n;} //    ,    
      return sum;
    }
  }
}
一つのタスクが完了すると、新しいタスクが自動的に起動します。
タスクが完了したら、他のタスクを起動できます。以下に前のコードを書き換えて、スレッドをブロックしません。

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
      t.Start();
      //t.Wait();
      Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}",t.Result));
      Console.ReadKey();
    }

    private static Int32 Sum(Int32 n)
    {
      Int32 sum = 0;
      for (; n > 0; --n)
        checked{ sum += n;} //    ,    
      return sum;
    }
  }
}
六、非同期に実行を依頼する
委託の非同期呼び出し:BeginnInvoke()とEnd Invoke()

namespace Test
{
  public delegate string MyDelegate(object data);
  class Program
  {
    static void Main(string[] args)
    {
      MyDelegate mydelegate = new MyDelegate(TestMethod);
      IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param");

      //      
      string resultstr = mydelegate.EndInvoke(result);
    }

    //    
    public static string TestMethod(object data)
    {
      string datastr = data as string;
      return datastr;
    }

    //      
    public static void TestCallback(IAsyncResult data)
    {
      Console.WriteLine(data.AsyncState);
    }
  }
}
七、スレッド同期
1)原子操作(Interlocked):すべての方法は原子の読み取りまたは書込み操作を行うことである。
2)lock()文:publicタイプをロックしないようにすると、インスタンスはコード制御の範囲を超えて、prvateオブジェクトを定義してロックします。
3)Monitorがスレッド同期を実現する
Monitor.Enter()とMonitor.Exit()により、排他的なリソースを取得し、他のスレッドへのアクセスを許可しません。
もう一つのTryEnter方法は、リソースが要求されない時はブロックされず、タイムアウト時間を設定して、直接falseに戻りません。
4)ReaderWriterLock
リソースの操作に対して読み書きが少ない場合は、リソースの利用率を高めるために、読み取り操作を共有ロックし、複数のスレッドを同時にリソースを読み取ることができ、書き込みは独占ロックとなり、スレッド1つのみの操作が可能となります。
5)イベント(Event)クラスの同期を実現する
イベントクラスには2つの状態があり、終了状態と非終了状態があり、終了状態でWaitOneを呼び出すと成功を要求することができ、Setで時間状態を終了状態に設定します。
1)AutoReetEvent(自動リセットイベント)
2)Manual ReseetEvent(手動リセットイベント)
6)信号量(Semaphore)
信号量はカーネルオブジェクトによって維持されるint変数で、0の場合、スレッドがブロックされ、0より大きい場合はブロックが解除され、1つの信号量の待ちスレッドがブロック解除されると、信号量カウント+1が加算されます。
スレッドはWaitOneで信号量を1つ減らし、Releaseで信号量を1つ加算して使うのが簡単です。
7)反発体(Mutex)
独占資源、使い方はSemaphoreに似ています。
 8)スパン間の同期
同期オブジェクトの名前を設定することで、システムレベルの同期が可能になります。異なるアプリケーションは、同期オブジェクトの名前で異なる同期オブジェクトを識別します。
以上はcxiマルチスレッドのプログラミングの詳細を深く理解しました。cxiマルチスレッドに関する資料は他の関連記事に注目してください。