Из Википедии, свободной энциклопедии
  (Перенаправлен с " Занятого ожидания" )
Перейти к навигации Перейти к поиску

В информатике и программной инженерии , занятом-ожидания , занят, перекручивание или спиннинг представляет собой метод , в котором процесс многократно проверяет, если условие истинно, например, является ли клавиатура ввода или замок доступен. Вращение также можно использовать для генерации произвольной задержки по времени, метод, который был необходим в системах, в которых отсутствовал метод ожидания определенного периода времени. Скорость процессора сильно различается от компьютера к компьютеру, особенно потому, что некоторые процессоры предназначены для динамической регулировки скорости в зависимости от текущей рабочей нагрузки [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  i  =  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 );  return  NULL ; }статическая  void  * f2 ( void  * p ) {  int  local_i  =  99 ;  сон ( 10 );  / *  засыпаем 10 секунд * / atomic_store ( & i ,  local_i );  printf ( "t2 изменил значение i на% d. \ n " ,  local_i );  return  NULL ; }int  main () {  int  rc ;  pthread_t  t1 ,  t2 ; rc  =  pthread_create ( & t1 ,  NULL ,  f1 ,  NULL );  если  ( rc  ! =  0 )  {  fprintf ( stderr ,  "pthread f1 failed \ n " );  вернуть  EXIT_FAILURE ;  } rc  =  pthread_create ( & t2 ,  NULL ,  f2 ,  NULL );  если  ( rc  ! =  0 )  {  fprintf ( stderr ,  "pthread f2 failed \ n " );  вернуть  EXIT_FAILURE ;  } pthread_join ( t1 ,  NULL );  pthread_join ( t2 ,  NULL );  put ( "Все потоки завершены." );  возврат  0 ; }

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

Альтернативы [ править ]

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

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

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

сон: hlt jmp  sleep

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

Соответствующее использование [ править ]

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

См. Также [ править ]

Ссылки [ править ]

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

Внешние ссылки [ править ]

  • Описание из Базовых спецификаций Open Group, выпуск 6, IEEE Std 1003.1, издание 2004 г.
  • Статья Герта Боддэрта « Спиновые блокировки на уровне пользователя - потоки, процессы и IPC »
  • Описание класса SpinLock в Австрии