マルチスレッドに深く入り込む:WaitとPulseを使っていくつかの同期構造の応用をシミュレーションします。


前の文章の中で、「マルチスレッドに深く入り込む:双方向信号と競争の使い方分析」はこのパターンに気づくかもしれません。二つのWaitingサイクルは以下の構造にします。

lock(_locker)
{
        while(!_flag) Monitor.Wait(_locker);
        _flag = false;
}

はここにあります。flagsは他のスレッドにtrueに設定されています。これは、役割から言えば、ここでAutoReetEventを模倣しています。もし私たちがflags=false;抜きにして、私達は基本的なManual ReseetEventを得ました。
WaitとPulseを使ってManual ReseetEventの残りのコードを完成しましょう。

readonly object _locker = new object();
        bool _signal;

        void WaitOne()
        {
            lock (_locker)
            {
                while (!_signal) Monitor.Wait(_locker);
            }
        }

        void Set()
        {
            lock (_locker) { _signal = true; Monitor.PulseAll(_locker); }
        }

        void Reset() { lock (_locker) _signal = false; }

ここでPulse Allを使っているのは、多くのスレッドが詰まっているからです。
WaitOneメソッドであれば_を追加します。signal=falseは、AutoReseetEventを簡単にシミュレートすることができます。例えば、

void WaitOne()
        {
            lock (_locker)
            {
                while (!_signal) Monitor.Wait(_locker);
                _signal = false; // |
            }
        }
は、Set方法でPulse AllをPulseに変更します。
ロックlocker){u}signal=true;Monitor.Pulselocker);
ご使用がintタイプの_です。signalマークなら、一番基本的なSemaphoreが得られます。
Waiting Queueues and Pulse All
もう一つのスレッドが同じ対象の上で待っていると、「待ち行列」ができます。
Pulseを呼び出すたびに、「待ち行列」の先頭にあるスレッドをリリースします。次の図形像の展示はこの点を示しています。

 
スレッドはMonitor.Enterを呼び出してReadyQueに入ります。ロックを取得して正常にロックを取得したら、その後Monitor.Exitを呼び出して終了します。
もしロックを取得したら他のスレッドや他のブロック条件を待つ必要があります。Waitメソッドを呼び出して待ち行列に入ります。
待ちスレッドが完了し、Pulseを呼び出した後、WaitingQueの先頭にあるスレッドはPulseによって、CPUのスケジュールを待っています。その後再度Ready Queに入り、再びロックを取得します。
カウントダウン
WaitとPulseを利用して、CountdownEventの主な機能を実現できます。例えば、

class Countdown
    {
        object _locker = new object();
        int _value; // _value

        public Countdown() { }
        public Countdown(int initialCount) { _value = initialCount; }

        public void Singnal() { AddCount(-1); } //

        public void AddCount(int amount)
        {
            lock (_locker)
            {
                _value += amount; //
                if (_value <= 0) Monitor.PulseAll(_locker);// value<=0, 。
            }
        }

        public void Wait()
        {
            lock (_locker)
            {
                // > 0 。
                while (_value > 0)
                {
                    Monitor.Wait(_locker);
                }
            }
        }
}

これは前回のコードとほぼ一致していますが、今回の渋滞条件は整体に基づいています。value?マーク