Холостой цикл
Холостой цикл (также "холостое ожидание", англ. busy waiting) — реализация ожидания в компьютерной программе, в котором проверка определённого условия осуществляется в бесконечном цикле. Выход из бесконечного цикла происходит только при удовлетворении проверяемого условия.
Также холостой цикл может использоваться для создания произвольной задержки выполнения программы.
В большинстве случаев холостой цикл считается антипаттерном, которого нужно избегать путём реорганизации кода или использование иного подхода к разработке (асинхронное выполнение, событийно-ориентированное программирование и т.п.).
Пример реализации на СиПравить
Во фрагменте кода ниже один из потоков ожидает значения 0 в переменной i и только после этого продолжает исполнение:
# include <pthread.h>
# include <stdatomic.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
/* i is global, so it is visible to all functions. It makes use of the special
* type atomic_int, which allows atomic memory accesses.
*/
atomic_int i = 0;
/* f1 uses a spinlock to wait for i to change from 0. */
static void *f1(void *p)
{
int local_i;
/* Atomically load current value of i into local_i and check if that value
is zero */
while ((local_i = atomic_load(&i)) == 0) {
/* do nothing - just keep checking over and over */
}
printf("i's value has changed to %d.\n", local_i);
return NULL;
}
static void *f2(void *p)
{
int local_i = 99;
sleep(10); /* sleep for 10 seconds */
atomic_store(&i, local_i);
printf("t2 has changed the value of i to %d.\n", local_i);
return NULL;
}
int main()
{
int rc;
pthread_t t1, t2;
rc = pthread_create(&t1, NULL, f1, NULL);
if (rc != 0) {
fprintf(stderr, "pthread f1 failed\n");
return EXIT_FAILURE;
}
rc = pthread_create(&t2, NULL, f2, NULL);
if (rc != 0) {
fprintf(stderr, "pthread f2 failed\n");
return EXIT_FAILURE;
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
puts("All pthreads finished.");
return 0;
}
Примеры реализации на JavaПравить
Данная реализации использует обращение к методу Thread.sleep() в цикле, что позволяет приостановить исполнение потока на заданное количество миллисекунд:
long delay = 1L; // время в миллескундах
volatile boolean waitForEvent = true; // значение выставляется из других потоков
while (waitForEvent) {
Thread.sleep(delay);
}
При этом планировщик отдаёт вычислительные ресурсы другим потокам, из-за чего "усыпление" и "побудка" потока являются дорогостоящими операциями. Другим недостатком данного способа является необходимость обработки исключения, а также невозможность приостановить поток менее чем на 1 миллисекунду. Начиная с Java 9 появился метод Thread.onSpinWait(), который позволяет реализовать непродолжительное ожидание без приостановки потока:
volatile boolean waitForEvent = true; // значение выставляется из других потоков
while (waitForEvent) {
Thread.onSpitWait();
}
Преимуществом данного подхода является возможность мгновенно прервать ожидание и продолжить выполнение.
Низкоуровневое применениеПравить
Одним из подвидов холостого ожидания является спин-блокировка.
В низкоуровневом программировании холостые циклы находят более широкое применение. На практике прерывание не всегда желательно для некоторых аппаратных устройств. Например, при необходимости записи некоторой контрольной информации в устройство и получения отклика об итогах записи, разработчик может обратится к функции задержки на уровне ОС, однако её вызов может потребовать больше времени, поэтому используется цикл активного ожидания.