stringtranslate.com

Занято ожиданием

В информатике и разработке программного обеспечения ожидание занятости , циклическое воспроизведение или вращение — это метод, при котором процесс неоднократно проверяет, истинно ли условие, например, доступен ли ввод с клавиатуры или блокировка . Вращение также можно использовать для создания произвольной временной задержки - метод, который был необходим в системах, в которых не было метода ожидания определенного периода времени. Скорость процессора сильно различается от компьютера к компьютеру, особенно потому, что некоторые процессоры предназначены для динамической регулировки скорости в зависимости от текущей рабочей нагрузки. [1] Следовательно, вращение как метод задержки может привести к непредсказуемым или даже противоречивым результатам в разных системах, если не включен код для определения времени, которое процессор тратит на выполнение цикла «ничего не делать» , или код цикла явно не проверяет реальное значение . -часы времени .

В большинстве случаев вращение считается антишаблоном, и его следует избегать, [2] поскольку процессорное время, которое можно было бы использовать для выполнения другой задачи , вместо этого тратится на бесполезную деятельность. Вращение может быть действенной стратегией в определенных обстоятельствах, особенно при реализации спин-блокировок в операционных системах, предназначенных для работы в системах SMP .

Пример кода C

Следующие примеры кода на C иллюстрируют два потока, которые совместно используют глобальное целое число i . Первый поток использует ожидание занятости, чтобы проверить изменение значения i :

#include <pthread.h> #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h>     /* i является глобальным, поэтому его видят все функции. Он использует специальный тип *atomic_int, который обеспечивает атомарный доступ к памяти. */ atomic_int я = 0 ;   /* f1 использует спин-блокировку, чтобы дождаться изменения i с 0. */ static void * f1 ( void * p ) { int local_i ; /* Атомарно загружаем текущее значение i в local_i и проверяем, равно ли это значение  нулю */ while (( local_i = atomic_load ( & i )) == 0 ) { /* ничего не делаем — просто продолжаем проверять снова и снова */ }                printf ( "Значение i изменилось на %d. \n " , local_i ); вернуть НУЛЬ ; }   static void * f2 ( void * p ) { int local_i = 99 ; спать ( 10 ); /* спящий режим на 10 секунд */ atomic_store ( & i , local_i ); printf ( "t2 изменил значение i на %d. \n " , local_i ); вернуть НУЛЬ ; }               int main () { int rc ; pthread_t t1 , t2 ;       rc = pthread_create ( & t1 , NULL , f1 , NULL ); if ( rc != 0 ) { fprintf ( stderr , "pthread f1 не удалось \n " ); вернуть EXIT_FAILURE ; }                rc = pthread_create ( & t2 , NULL , f2 , NULL ); if ( rc != 0 ) { fprintf ( stderr , "pthread f2 не удалось \n " ); вернуть EXIT_FAILURE ; }                pthread_join ( t1 , NULL ); pthread_join ( t2 , NULL ); puts ( "Все pthreads завершены." ); вернуть 0 ; }      

В таком случае использования можно рассмотреть возможность использования переменных состояния C11 .

Альтернативы

Большинство операционных систем и библиотек потоков предоставляют различные системные вызовы , которые блокируют процесс по событию, например получение блокировки, изменение таймера, доступность ввода-вывода или сигналы . Использование таких вызовов обычно дает самый простой, наиболее эффективный, справедливый и свободный от гонок результат. Одиночный вызов проверяет, информирует планировщик об ожидаемом событии, вставляет барьер памяти , где это применимо, и может выполнить запрошенную операцию ввода-вывода перед возвратом. Другие процессы могут использовать ЦП, пока вызывающий объект заблокирован. Планировщику предоставляется информация, необходимая для реализации наследования приоритетов или других механизмов, позволяющих избежать истощения .

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

В программах, которые никогда не заканчиваются (например, в операционных системах), бесконечное ожидание занятости может быть реализовано с помощью безусловных переходов, как показано в этом синтаксисе NASMjmp $ : . ЦП безоговорочно перейдет на свою позицию навсегда. Такое напряженное ожидание можно заменить следующим:

сон: hlt jmp сон 

Дополнительную информацию см. в разделе HLT (инструкция x86) .

Надлежащее использование

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

Смотрите также

Рекомендации

  1. ^ «Технология Intel Turbo Boost» .
  2. ^ «Почему не следует использовать класс типа «летучий»» . Архивировано из оригинала 4 октября 2017 г. Проверено 10 июня 2013 г.

Внешние ссылки