Лучше опросить или подождать? - PullRequest
13 голосов
/ 02 июня 2009

Я видел вопрос о том, почему "опрос плох". С точки зрения минимизации количества процессорного времени, используемого одним потоком, было бы лучше сделать ожидание вращения (то есть опрос для требуемого изменения в цикле while) или ожидание на объекте ядра (например, объект события ядра в окнах)

Для контекста предположим, что код потребуется для запуска на процессоре любого типа, одноядерном, многопоточном, многоядерном и т. Д. Также предположим, что поток, который будет опрашивать или ждать, не сможет продолжаться, пока результат опроса не будет удовлетворительным если бы опросили вместо ожидания. Наконец, время между тем, когда поток начинает ждать (или опрос), и когда условие выполняется, потенциально может варьироваться от очень короткого до долгого времени.

Поскольку операционная система, вероятно, будет более эффективно "опрашивать" в случае "ожидания", я не хочу видеть аргумент "ожидание просто означает, что кто-то делает опрос", это старые новости, и это не обязательно 100% точность.

Ответы [ 8 ]

18 голосов
/ 02 июня 2009

При условии, что ОС имеет разумную реализацию примитивов параллелизма такого типа, определенно лучше подождать с объектом ядра.

Помимо прочего, это позволяет ОС знать, что не нужно планировать рассматриваемый поток для дополнительных временных интервалов, пока ожидаемый объект не будет в соответствующем состоянии. В противном случае у вас есть поток, который постоянно переносится по расписанию, переключается на контекст, а затем запускается какое-то время.

Вы специально спрашивали о минимизации времени процессора для потока: в этом примере блокировка потока на объекте ядра будет использовать нулевое время; поток опроса будет использовать все виды времени.

Кроме того, аргумент "кто-то еще опрашивает" не обязательно должен быть правдой. Когда объект ядра входит в соответствующее состояние, ядро ​​может посмотреть в этот момент, какие потоки ожидают этот объект ... и затем запланировать выполнение одного или нескольких из них. В этом случае ядру (или кому-либо еще) не нужно ничего опрашивать.

9 голосов
/ 02 июня 2009

Ожидание - это «лучший» способ вести себя. Когда вы ожидаете объекта ядра, вашему потоку не будет предоставлено никакого процессорного времени, поскольку планировщик знает, что работа не готова. Ваш поток получит процессорное время только тогда, когда будет выполнено условие ожидания. Это означает, что вы не будете загружать ресурсы процессора без необходимости.

3 голосов
/ 02 июня 2009

Я думаю, что вопрос, который еще не был затронут, заключается в том, что, если у вашей ОС много работы, блокировка приводит к тому, что ваш поток переходит к другому процессу. Если все процессы используют блокирующие примитивы там, где они должны (например, ожидание ядра, файловый / сетевой ввод-вывод и т. Д.), Вы даете ядру больше информации, чтобы выбрать, какие потоки должны выполняться. Таким образом, он будет выполнять больше работы за то же время. Если ваше приложение может делать что-то полезное в ожидании открытия этого файла или доставки пакета, то даже вам поможет ваше приложение.

2 голосов
/ 02 июня 2009

Есть только несколько мест, обычно внутри вещей низкого уровня ОС (обработчики прерываний / драйверы устройств), где ожидание вращения имеет смысл / требуется. Приложениям общего назначения всегда лучше ждать некоторых примитивов синхронизации, таких как взаимные исключения / условные переменные / семафоры.

2 голосов
/ 02 июня 2009

Ожидание требует больше ресурсов и означает дополнительное переключение контекста. Действительно, некоторые примитивы синхронизации, такие как CLR Monitors и Win32 критические секции, используют протокол двухфазной блокировки - некоторое ожидание вращения выполняется, прежде чем действительно произойдет настоящее ожидание.

Я полагаю, что сделать двухэтапную работу было бы очень сложно и потребовало бы много испытаний и исследований. Так что, если у вас нет времени и ресурсов, придерживайтесь оконных примитивов ... они уже сделали исследование для вас.

1 голос
/ 15 мая 2012

Существует четыре основных подхода:

  1. Использовать какой-нибудь ожидающий примитив ОС для ожидания события
  2. Используйте некоторый примитив таймера ОС, чтобы проверить с определенной частотой, произошло ли событие еще
  3. Повторно проверяйте, произошло ли событие, но используйте примитив ОС, чтобы получить временной интервал на произвольную и неизвестную продолжительность в любое время, когда это не произошло.
  4. Повторно проверяйте, произошло ли событие, не возвращая ЦП, если это не произошло.

Когда № 1 практичен, зачастую это лучший подход, если только задержка ответа на событие не может быть полезной. Например, если ожидается получение большого количества данных последовательного порта в течение нескольких секунд, и если обработка данных через 100 мс после их отправки будет столь же хороша, как и их мгновенная обработка, периодический опрос с использованием одного из двух последних подходы могут быть лучше, чем установка события «данные получены».

Подход № 3 довольно груб, но во многих случаях может быть хорошим. Зачастую это будет тратить больше процессорного времени и ресурсов, чем на первый шаг, но во многих случаях его будет проще реализовать, а расход ресурсов во многих случаях будет достаточно мал, чтобы не иметь значения.

Подход №2 часто более сложен, чем №3, но имеет преимущество в том, что может обрабатывать множество ресурсов с помощью одного таймера и без выделенного потока.

Подход № 4 иногда необходим во встроенных системах, но, как правило, он очень плохой, если только не выполняется непосредственный опрос аппаратного обеспечения, и у него не будет ничего полезного, пока не произойдет рассматриваемое событие. Во многих случаях не может произойти ожидаемое условие, пока поток, ожидающий его, не выдаст ЦП. Выход из CPU, как в подходе № 3, фактически позволит ожидающему потоку увидеть событие раньше, чем его включит.

1 голос
/ 02 июня 2009

Ожидание (блокирование) - почти всегда лучший выбор («лучший» в смысле эффективного использования ресурсов обработки и минимизации воздействия на другой код, работающий в той же системе). Основные исключения:

  1. Когда ожидаемая продолжительность опроса мала (аналогична по величине стоимости системного вызова блокировки).
  2. В основном во встраиваемых системах, когда ЦП выделен для выполнения конкретной задачи, а простоя ЦП бесполезна (например, некоторые программные маршрутизаторы, построенные в конце 90-х годов, использовали этот подход).

Как правило, опрос не используется в ядрах ОС для реализации блокирования системных вызовов - вместо этого события (прерывания, таймеры, действия в мьютексах) приводят к тому, что заблокированный процесс или поток становятся работоспособными.

1 голос
/ 02 июня 2009

Я согласен с Darksquid: если ваша ОС имеет примитивные примитивы параллелизма, вам не нужно опрашивать. Опрос обычно происходит самостоятельно в системах реального времени или на ограниченном оборудовании, на котором нет ОС, поэтому вам нужно провести опрос, потому что у вас может не быть возможности wait (), а также потому, что он дает вам точный контроль того, как долго Вы хотите подождать в определенном состоянии, а не во власти планировщика.

...