Более длинные сны (в C ++) менее точны, чем короткие - PullRequest
17 голосов
/ 08 июня 2011

У меня есть задача что-то делать каждую "круглую" минуту (в хх: хх: 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 секунд - количество прошедших секунд в текущей минуте). Но один программист сказал мне, что это не точно, и что я должен изменить его на цикл while со сном (1) внутри. Я считаю весьма сомнительным, что он прав, но я просто хотел проверить, кто-то знает, есть ли меньшая точность, если интервал сна велик. Я предполагаю, что сон реализован таким образом, что в определенное время в будущем триггер активируется, и поток помещается в «группу готовых к выполнению потоков», поэтому я не вижу причин для различия в точности. КСТАТИ ОС ОС Ubuntu, и я не забочусь об ошибках менее 2-3 секунд. Например, если я сплю в течение 52 секунд, 53,8 сна вполне приемлемы. Постскриптум Я знаю о сне, определяющем минимальное время, и что теоретически мой поток может быть активирован в 2047 году, но я спрашиваю о реалистичных сценариях.

Ответы [ 8 ]

7 голосов
/ 08 июня 2011

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

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

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

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

7 голосов
/ 08 июня 2011

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

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

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

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

4 голосов
/ 08 июня 2011

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

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

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

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

4 голосов
/ 08 июня 2011

сон не очень точен во многих случаях.Точность зависит от ОС.В 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: если вы хотите более высокую точность при долгом ожидании, поспите некоторое время и используйте разницу во времени на основе часов реального времени.Т.е. сохраняйте текущее время, когда вы начинаете спать, затем на каждом интервале проверяйте, как далеко вы находитесь от установленного времени ожидания.

2 голосов
/ 08 июня 2011

Ответ - да.Однако это не имеет ничего общего с C ++.Он имеет все, что связано с операционной системой.

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

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

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

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

2 голосов
/ 08 июня 2011

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

2 голосов
/ 08 июня 2011

Сон работает в терминах временных квантов планировщика, и, если вы не получите сигнал, вы не сможете проснуться до того, как этот квант будет израсходован.Кроме того, сон не предназначен быть точным или точным.Кроме того, время - это скорее ориентир, чем правило.Это обычно понимается как «по крайней мере, так много, но возможно в любое время дольше».
Следовательно, 60x sleep(1) никогда не может быть более точным, чем sleep(60).

Поскольку вы заявляете, что ваша ОСUbuntu, вы также можете использовать timerfd [1] .Установите время истечения на 1 минуту и ​​read() на нем.Если вы получите EINTR, просто read() снова.В противном случае вы знаете, что минута истекает.

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

2 голосов
/ 08 июня 2011

Если цель состоит в том, чтобы спать до определенного системного времени (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';
}

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

...