В сценарии использования Wait () и Pulse () мы можем заменить `while` на` if`? - PullRequest
0 голосов
/ 21 июня 2019

В интернете я видел много примеров с Wait () и Pulse (), и они использовали два while, как в этом примере:

class MyQueue
{
    private Queue<string> queue = new Queue<string>();
    private const int CAPACITY = 3;
    public void Put(string element)
    {
        lock (this)
        {
            // first `while`
            while (queue.Count == CAPACITY)
            {
                Monitor.Wait(this);
            }
            queue.Enqueue(element);
            Console.WriteLine($"Put {element} ({queue.Count})");
            Monitor.Pulse(this);
        }
    }
    public string Take()
    {
        lock (this)
        {
            // second `while`
            while (queue.Count == 0)
            {
                Monitor.Wait(this);
            }
            string element = queue.Dequeue();
            Console.WriteLine($"Taked {element} ({queue.Count})");
            Monitor.Pulse(this);
            return element;
        }
    }
}

В Main ():

MyQueue queue = new MyQueue();
new Thread(new ThreadStart(() => {
    queue.Take();
    queue.Take();
})).Start();
new Thread(new ThreadStart(() => {
    queue.Put("a");
    queue.Put("b");
    queue.Put("c");
    queue.Put("d");
    queue.Put("e");
    queue.Put("f");
})).Start();

Мне кажется, я понял сценарий использования Pulse () и Wait ().

В приведенном выше примере, я думаю, что все два while можно заменить на if.Я попытался, и он также напечатал тот же результат.

Это правильно?Спасибо.

1 Ответ

1 голос
/ 21 июня 2019

В вашем конкретном примере, вероятно, было бы хорошо сделать, как вы предлагаете. У вас ровно один производитель и один потребитель, и поэтому они всегда должны работать согласованно друг с другом, чтобы гарантировать, что поток пробуждается, только если условие его ожидания разрешено.

Тем не менее:

  1. Реализации производителя и потребителя небезопасны для использования if, если у вас более одного производителя или потребителя. Это связано с тем, что потоки могут быть запущены, и один поток может быть запущен, но затем не запланирован, пока другой поток каким-либо образом не аннулирует первоначальное разрешение условия ожидания.
  2. Хотя я скептически отношусь к тому, что класс .NET Monitor подвержен проблеме ложных пробуждений & mdash; то есть поток в состоянии ожидания разбудился из-за некоторого события, отличного от явного пробуждения взаимодействующим потоком (например, вызов Monitor.Pulse()) & mdash; люди, которые знают параллельное программирование и C # намного лучше, чем я, сказали иначе (см., например, C # Monitor.Wait () страдает от ложных пробуждений? ). И если вас вообще беспокоят ложные пробуждения, вам понадобится цикл вместо простого if, чтобы убедиться, что вы перепроверили условие ожидания перед продолжением, на случай, если оно на самом деле не было выполнено до того, как вы нить проснулась.

См. Также статью Эрика Липперта Наблюдайте за безумием, часть вторая .

Все это говорит о том, что сценарий «производитель / потребитель» гораздо проще реализовать в современном .NET / C # с помощью BlockingCollection<T>. Вы даже можете указать максимальную длину для очереди при создании коллекции, чтобы обеспечить поведение «блок при заполнении», наблюдаемое в вашем примере кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...