5

http://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystified

キュー:

レディキューは、特定のロックを待機しているスレッドのコレクションです。Monitor.Waitメソッドは、別のキュー、つまり待機キューを導入します。パルスを待つことは、ロックを取得することを待つこととは異なるため、これが必要です。レディキューと同様に、待機キューはFIFOです。

推奨パターン:

これらのキューは、予期しない動作を引き起こす可能性があります。パルスが発生すると、待機キューの先頭が解放され、準備完了キューに追加されます。ただし、レディキューに他のスレッドがある場合、それらは解放されたスレッドの前にロックを取得します。ロックを取得するスレッドは、パルススレッドが依存する状態を変更する可能性があるため、これは問題です。解決策は、lockステートメント内でwhile条件を使用することです。

*Q=キュー。

それによって、私が電話をするときPulse、それが終わる前にそれが2つのことをすることを私は理解します。まず、待機中のQから準備完了のQに1つのスレッドを削除します。次に、準備完了のQの1つのスレッド(そのスレッドが誰であるかを知らなくても)にロックを取得させます。誰がロックを取得するかは関係ありません(待機中のQから来たスレッド、または何らかの理由で準備完了のQにあったスレッド)。

私がそれについて正しければ、なぜwhile問題を解決するのを助ける前に置くのですかMonitor.Wait(問題-待機中のQから来たスレッドがロックを取得しなかったとしてもパルスは終了します)?

A。 _ の目的について正しいかどうか教えてくださいMonitor.Pulse

B。 _ なぜwhile前に置く必要があるのですかMonitor.Wait

以下の答えの完全なコード:

class Program
{
    static Queue<int> queue = new Queue<int>();
    static object someMonitor = new object();
    
    static void Main(string[] args)
    {
        Thread Thread1 = new Thread(WorkAlltheTime);
        Thread1.Name = "Thread1";
        Thread Thread2 = new Thread(WorkAlltheTime);
        Thread2.Name = "Thread2";
        Thread Thread3 = new Thread(WorkOnce);
        Thread3.Name = "Thread3";
        Thread1.Start();
        Thread2.Start();
        Thread.Sleep(1000);
        Thread3.Start();
        Console.ReadLine();
    }
    
    static void WorkAlltheTime()
    {
        Console.WriteLine("Came in to Ready Q: " + Thread.CurrentThread.Name);
        lock (someMonitor)
        {
            Console.WriteLine("Came out from Ready Q: " + Thread.CurrentThread.Name);
            // Broken!
            while (queue.Count == 0)
            {
                Console.WriteLine("Came in to Waiting Q: " + Thread.CurrentThread.Name);
                Monitor.Wait(someMonitor);
                Console.WriteLine("Came out from Waiting Q: " + Thread.CurrentThread.Name);
            }
            queue.Dequeue();
            Console.WriteLine("Thread: "+Thread.CurrentThread.Name+" Pulled Out");
        }
    }
    
    static void WorkOnce()
    {
        lock (someMonitor)
        {
            queue.Enqueue(1);
            Monitor.Pulse(someMonitor);
        }
    }   
}
4

1 に答える 1

10

生産者/消費者キューを作成しようとしていると想像してください。Pulseアイテムを作成するたびに、消費者は消費するアイテムができるまで待つ必要があります。次のようなコードを記述します。

Foo item;
lock(someMonitor)
{
    while (queue.Count == 0)
    {
        Monitor.Wait(someMonitor);
    }
    item = queue.Dequeue();
}
// Use the item

whileループがなく、代わりに次のように記述したとします。

Foo item;
lock(someMonitor)
{
    // Broken!
    if (queue.Count == 0)
    {
        Monitor.Wait(someMonitor);
    }
    item = queue.Dequeue();
}
// Use the item

ここで、1つのスレッドがすでに待機していて、lockステートメントの直前に別のスレッドがあるとします...次に、プロデューサーがモニターにパルスを送ります(もちろん、キューにアイテムを追加します)。

その時点で、まだロックに到達していないスレッドが最初にロックを取得することは完全に実行可能です...その時点で、「待機中」のスレッドがロックを取得するまでに、キューは次のようになります。再び空になります。ループせずに1つのifステートメントを使用するだけで、キューが空のときにデキューすることになり、失敗します。

whileループを使用すると、次のアイテムが生成されるまで再度待機します。これは、本当に必要なものです。

于 2012-08-25T10:41:48.467 に答える