いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-046 (System.Threading.CountdownEvent(4))(AddCount, Reset, .NET 4.0)


今回も、CountdownEventクラスについて。
CountdownEventクラスは、.NET 4.0からSystem.Threading名前空間に追加されたクラスです。


今回は、AddCountとResetメソッドについてです。


CountdownEventには、AddCountメソッドというのが存在していて
内部のカウンタを後から増やすことが出来ます。
実際に実行時にならないとカウンタを何個にすればいいのか分からない場合などに
利用します。


注意点として、以下の場合にAddCountを行うと例外が発生します。

既にCurrentCountが0の状態で、AddCountしようとした場合

つまり、IsSetがTrueになっている状態でAddCountは出来ないということになります。


Resetメソッドは、名前の通りの機能です。


以下、サンプルです。
以下のサンプルでは、N個の処理を実行して
処理が開始される度に、AddCountにてカウントをインクリメントし
処理終了と共に、Signalでデクリメントしています。

    #region CountdownEventSamples-04
    /// <summary>
    /// CountdownEventクラスについてのサンプルです。
    /// </summary>
    /// <remarks>
    /// CountdownEventクラスは、.NET 4.0から追加されたクラスです。
    /// </remarks>
    public class CountdownEventSamples04 : IExecutable
    {
        public void Execute()
        {
            //
            // CountdownEventクラスには、以下のメソッドが存在する。
            //     ・AddCountメソッド
            //     ・Resetメソッド
            // AddCountメソッドは、CountdownEventの内部カウントをインクリメントする。
            // Resetメソッドは、現在の内部カウントをリセットする。
            //
            // どちらのメソッドも、Int32を引数に取るオーバーロードが用意されており
            // 指定した数を設定することも出来る。
            //
            // 尚、AddCountメソッドを利用する際の注意点として
            //     既に内部カウントが0の状態でAddCountを実行すると例外が発生する。
            // つまり、既にIsSetがTrue(シグナル状態)でAddCountするとエラーとなる。
            //
            
            //
            // 内部カウントが0の状態で、AddCountしてみる.
            //
            using (CountdownEvent cde = new CountdownEvent(0))
            {
                // 初期の状態を表示.
                PrintCurrentCountdownEvent(cde);
                
                try
                {
                    //
                    // 既にシグナル状態の場合に、さらにAddCountしようとすると例外が発生する.
                    //
                    cde.AddCount();
                }
                catch (InvalidOperationException invalidEx)
                {
                    Console.WriteLine("*** {0} ***", invalidEx.Message);
                }
                
                // 現在の状態を表示.
                PrintCurrentCountdownEvent(cde);
            }
            
            Console.WriteLine("");
            
            using (CountdownEvent cde = new CountdownEvent(1))
            {
                // 初期の状態を表示.
                PrintCurrentCountdownEvent(cde);
                
                //
                // 10個の別処理を実行する.
                // それぞれの内部処理にてランダムでSLEEPして、終了タイミングをバラバラに設定.
                //
                Console.WriteLine("別処理開始・・・");
                
                for (int i = 0; i < 10; i++)
                {
                    Task.Factory.StartNew(TaskProc, cde);
                }
                
                do
                {
                    // 現在の状態を表示.
                    PrintCurrentCountdownEvent(cde, "\t");
                    
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                }
                while (cde.CurrentCount != 1);
                
                Console.WriteLine("・・・別処理終了");
                
                //
                // 待機.
                //
                Console.WriteLine("メインスレッドにて最後のカウントをデクリメント");
                cde.Signal();
                cde.Wait();
                
                // 現在の状態を表示.
                PrintCurrentCountdownEvent(cde);

                Console.WriteLine("");
                
                //
                // 内部カウントをリセット.
                //
                Console.WriteLine("内部カウントをリセット");
                cde.Reset();
                
                // 現在の状態を表示.
                PrintCurrentCountdownEvent(cde);
                
                //
                // 待機.
                //
                Console.WriteLine("メインスレッドにて最後のカウントをデクリメント");
                cde.Signal();
                cde.Wait();
                
                // 現在の状態を表示.
                PrintCurrentCountdownEvent(cde);
            }
        }
        
        void PrintCurrentCountdownEvent(CountdownEvent cde, string prefix = "")
        {
            Console.WriteLine("{0}InitialCount={1}", prefix, cde.InitialCount);
            Console.WriteLine("{0}CurrentCount={1}", prefix, cde.CurrentCount);
            Console.WriteLine("{0}IsSet={1}",        prefix, cde.IsSet);
        }
        
        void TaskProc(object data)
        {
            //
            // 処理開始と共に、CountdownEventの内部カウントをインクリメント.
            //
            CountdownEvent cde = data as CountdownEvent;
            cde.AddCount();
            
            Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(10)));
            
            //
            // 内部カウントをデクリメント.
            //
            cde.Signal();
        }
    }
    #endregion


実行結果は以下のようになります。

  InitialCount=0
  CurrentCount=0
  IsSet=True
  *** イベントは既に通知された状態であり、インクリメントできません。 ***
  InitialCount=0
  CurrentCount=0
  IsSet=True
  
  InitialCount=1
  CurrentCount=1
  IsSet=False
  別処理開始・・・
  	InitialCount=1
  	CurrentCount=1
  	IsSet=False
  	InitialCount=1
  	CurrentCount=4
  	IsSet=False
  	InitialCount=1
  	CurrentCount=7
  	IsSet=False
  	InitialCount=1
  	CurrentCount=5
  	IsSet=False
  	InitialCount=1
  	CurrentCount=5
  	IsSet=False
  	InitialCount=1
  	CurrentCount=3
  	IsSet=False
  	InitialCount=1
  	CurrentCount=2
  	IsSet=False
  ・・・別処理終了
  メインスレッドにて最後のカウントをデクリメント
  InitialCount=1
  CurrentCount=0
  IsSet=True
  
  内部カウントをリセット
  InitialCount=1
  CurrentCount=1
  IsSet=False
  メインスレッドにて最後のカウントをデクリメント
  InitialCount=1
  CurrentCount=0
  IsSet=True


AddCountする処理にて、カウントが上がっていき、それから下がって行っている状態が分かります。


================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。