Подтвердить что ты не робот

Более длинные спящие (на С++) менее точные, чем короткие

У меня есть задача сделать что-то каждую "круглую" минуту (в xx: xx: 00) И я использую что-то вроде

const int statisticsInterval=60;
    time_t t=0;
    while (1)
    {
        if (abs(t-time(NULL)==0))   //to avoid multiple calls in the same second that is the multiple of 60
                boost::this_thread::sleep(boost::posix_time::seconds(2));//2, not 1 to make sure that 1 second passes

        t=time(NULL);
        boost::this_thread::sleep(boost::posix_time::seconds(statisticsInterval-(t%statisticsInterval)));

        //DO WORK
    }

Как вы можете видеть, я использую сон (60 секунд - количество прошедших секунд в текущей минуте). Но один программист сказал мне, что он не является точным и что я должен изменить его на в то время как петля со сном (1) внутри. Я считаю очень сомнительным, что он прав, но я просто хотел проверить, знает ли кто-нибудь, есть ли меньше точности, если спящий интервал длинный. Я предполагаю, что сон реализован таким образом, что в определенное время в будущем триггер активируется, и поток помещается в "готовую к исполнению группу потоков", поэтому я не вижу причин для разницы в точности. BTW OS - ubuntu, и я не забочусь о менее чем 2-3-секундных ошибках. Например, если я сплю в течение 52 секунд, 53,8 сна вполне приемлемы. Постскриптум Я знаю о сне, определяющем минимальное время, и что теоретически мой поток может активироваться в год 2047., но я спрашиваю о реалистичных сценариях.

4b9b3361

Ответ 1

Когда вы выполняете спящий режим (N), он сообщает ОС, чтобы вызвать поток в текущее время + N.

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

Во всяком случае, не должно быть никаких проблем с точностью, потому что метод не имеет ничего общего с N.

Единственная причина, по которой он не будет "точным", - это то, что это дерьмовая ОС, которая не может правильно рассчитать время. И опять же, цикл не решит этого.

Ответ 2

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

Однако API-интерфейс ускоренного потока this_thread::sleep() не документирован, чтобы иметь эти ранние пробуждения, и поэтому этот метод не нужен (API ускорения потока выполняет цикл для вас).

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

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

Ответ 3

сон не очень точен во многих случаях. Насколько это точно, зависит от ОС. Думаю, в Windows 7 разрешение таймера составляет около 15,4 мс. Кроме того, вы можете обычно указывать планировщику, как справляться с задержкой спящего режима...

Вот хороший текст:

Linux: http://linux.die.net/man/3/nanosleep

Windows: http://msdn.microsoft.com/en-us/library/ms686298(v=vs.85).aspx

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

Ответ 4

Boost.Thread реализация сна для систем POSIX может использовать разные подходы к спящему:

  • Временное ожидание мьютекса в случае, когда поток создается с помощью Boost.Thread и имеет определенную информацию о потоке.
  • Используйте pthread_delay_np, если они доступны, и поток не создается с помощью Boost.Thread.
  • USe nanosleep, если pthread_delay_np недоступен.
  • Создайте локальный мьютекс и выполните ожидание на нем (в худшем случае, если ничего не доступно).

Случаи номер 2, 3 и 4 выполняются в петле в 5 раз (с точки зрения Boost 1.44). Поэтому, если спящий поток прерывается (т.е. С некоторым сигналом) более 5 раз - может возникнуть потенциальная проблема. Но это вряд ли произойдет.

Во всех случаях точность будет намного выше, чем вторая, поэтому выполнение нескольких спящих не будет более точным, чем длинный. Вы можете быть обеспокоены только тем, что ваша программа полностью выгружена из-за длительного сна. Например, если машина настолько занята, поэтому ядро ​​ставит всю программу на диск. Чтобы избежать поменять местами, вы должны вращаться (или иногда делать небольшие сны и просыпаться). Обычно, если производительность имеет большое значение, программы вращаются на процессоре и никогда не вызывают сон, потому что следует избегать любого блокирующего вызова. Но это правда, если мы говорим nano/micro-seconds.

Ответ 5

Sleep работает в терминах квантов времени планировщика (Правка: Между тем, большинство операционных систем поддерживает "тикающие" планировщики, то есть больше нет фиксированных квантов, однако принцип остается верным... есть объединение таймеров и прочее).

Если вы не получите сигнал, вы не сможете проснуться до того, как этот квант будет израсходован. Кроме того, sleep не предназначен, чтобы быть точным или точным. Кроме того, время - это скорее ориентир, чем правило.

Хотя вы можете думать о времени сна в терминах "будет продолжаться после времени X", это совсем не то, что происходит. Технически, sleep работает в терминах "пометить поток не готовым в течение приблизительно времени X, затем отметить его готовым, вызвать планировщик, и тогда мы посмотрим, что произойдет". Обратите внимание на тонкую разницу между "готовностью" и фактическим бегом. Поток в принципе может быть готов очень долго и никогда не запускаться.
Следовательно, 60x sleep(1) не может никогда быть более точным, чем sleep(60). Это сделает поток не готовым и готовым снова 60 раз, и он вызовет планировщик 60 раз. Поскольку планировщик не может работать в нулевое время (и поток не может быть подготовлен в нулевое время, а также нельзя переключать контекст в нулевое время), многократный сон в течение коротких периодов времени обязательно должен занимать больше времени, чем однократный сон в течение кумулятивного времени. на практике.

Поскольку вы заявляете, что ваша ОС - Ubuntu, вы также можете использовать timerfd[1]. Установите время истечения до 1 минуты и read() на нем. Если вы получите EINTR, просто read() снова. В противном случае, вы знаете, что минута истекла. Использование таймера - это правильная вещь, если вы хотите точного хронометража (на физическом компьютере он не может быть и никогда не будет идеальным на 100,00%, но он будет настолько хорош, насколько это возможно, и он позволит избежать других систематических ошибок, особенно с повторяющимися событиями).
Функция POSIX timer_create также будет работать, она более переносима, и она может занимать до половины микросекунды или около того меньше издержек (может быть! Может быть, нет!), Но она не так удобна и гибка, как timerfd.

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

Ответ 6

В общем, Sleep - это не правильный метод для выбора времени. Лучше использовать прецизионный таймер с функцией обратного вызова. В Windows можно использовать таймеры "Мультимедиа", которые имеют разрешение не более 1 мс на большинстве аппаратных средств. см. здесь. Когда таймер истекает, ОС вызывает функцию обратного вызова в близком к реальному времени. см. здесь.

Ответ 7

Если цель состоит в том, чтобы спать до заданного системного времени (xx: xx: 00), рассмотрите возможность использования перегрузки boost::this_thread::sleep, которая занимает время, как в boost::posix_time::ptime, а не продолжительность.

например,

#include <iostream>
#include <boost/date_time.hpp>
#include <boost/thread.hpp>
int main()
{
    using namespace boost::posix_time;
    ptime time = boost::get_system_time();
    std::cout << "time is " << time << '\n';
    time_duration tod = time.time_of_day();
    tod = hours(tod.hours()) + minutes(tod.minutes() + 1);
    time = ptime(time.date(), tod);
    std::cout << "sleeping to  " << time << "\n";
    boost::this_thread::sleep(time);
    std::cout << "now the time is " << boost::get_system_time() << '\n';
}

в С++ 0x этим двум перегрузкам были даны разные имена: std::this_thread::sleep_for() и std::this_thread::sleep_until();

Ответ 8

Ответ: да. Это не имеет ничего общего с С++. Он имеет все, что связано с операционной системой.

Из-за большей концентрации внимания на использовании малой мощности в современных портативных системах операционные системы становятся более умными в отношении таймеров.

Оба Windows и Linux используют таймер чтобы не просыпаться слишком часто. Этот провал автоматически рассчитывается с использованием продолжительности таймаута. Его можно переопределить по-разному, если абсолютно точный таймер абсолютно необходим.

Что это делает для операционной системы, так это позволить ему войти в действительно глубокие состояния сна. Если таймеры все время уходят, процессор и ОЗУ не получают возможности отключиться. Но если таймеры собираются вместе в пакет, процессор может включить питание, запустить все операции таймера, а затем снова отключить питание.

Итак, если есть 10 программ, все спящие в течение 60 секунд, но смещенные на полсекунды или около того, наиболее эффективное использование ЦП состоит в том, чтобы просыпаться один раз, запускать все 10 таймеров, а затем возвращаться к сна вместо пробуждение 10 раз.