Pulse или PulseAll?

В нашем примере есть еще одна возможность для экономии сигналов. После добавления задачи в очередь можно вызвать Pulse, а не PulseAll, и ничего не сломается.

Вспомним отличие: при использовании Pulse может пробудиться максимум один поток (и перепроверить условие блокирования в while); в случае PulseAll пробудятся все ждущие потоки (и перепроверят условие блокирования). Если в очередь добавляется одна задача, для обработки нужен только один потребитель, так что нужно разбудить только одного вызовом Pulse. Это походит на класс спящих детей – если есть только одно мороженое, нет смысла будить их всех и ставить в очередь.

В нашем примере запускаются только два потока-потребителя, так что большой выгоды не извлечь. Если бы потоков-потребителей было десять, некоторый выигрыш от выбора Pulse вместо PulseAll появился бы. Если в очередь добавляются несколько задач, необходимо вызвать Pulse несколько раз. Это можно сделать в рамках одной конструкции lock, например, так:

lock (locker) { taskQ.Enqueue("task 1"); taskQ.Enqueue("task 2"); Monitor.Pulse(locker); // "Сигналим двум Monitor.Pulse(locker); // ожидающим потокам." }

Цена одного невызванного Pulse – застрявший поток-потребитель. Эта ошибка будет блуждающей, так как вызов Pulse производит эффект, только когда потребитель находится в состоянии ожидания. Следовательно, можно расширить наш предыдущий лозунг “Сомневаешься – сигналь” до “Сомневаешься – сигналь всем!”

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: