Спать в течение точного времени - PullRequest
7 голосов
/ 06 марта 2011

Мое понимание функции Sleep состоит в том, что она следует "по крайней мере семантике", то есть sleep (5) гарантирует, что поток спит в течение 5 секунд, но он может оставаться заблокированным в течение более 5 секунд, в зависимости от других факторов. Есть ли способ спать ровно в указанный период времени (без занятого ожидания).

Ответы [ 7 ]

14 голосов
/ 06 марта 2011

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

Однако ... хотя и не идеально, вы можете получить МНОГО лучших результатов, чем "нормальных", просто повысив приоритет процесса, который требует лучшего времени . В Windows вы можете достичь этого с помощью функции SetPriorityClass. Если вы установите приоритет на самый высокий уровень (REALTIME_PRIORITY_CLASS: 0x00000100), вы получите намного лучшие результаты синхронизации. Опять же - это не будет идеально, как вы просите, однако.

Это также возможно на других платформах, кроме Windows, но у меня никогда не было причин делать это, поэтому я не проверял это.

РЕДАКТИРОВАТЬ: Согласно комментарию Энди Т, если ваше приложение является многопоточным, вам также нужно следить за приоритетом, назначенным потокам. Для Windows это задокументировано здесь .


Некоторый фон ...

Некоторое время назад я использовал SetPriorityClass, чтобы повысить приоритет приложения, в котором я выполнял анализ высокоскоростного видео в реальном времени, и я НЕ мог пропустить кадр. Кадры поступали на компьютер с очень обычной (управляемой внешним HW) частотой 300 кадров в секунду, что вызывало прерывание HW для каждого кадра, который я затем обслуживал. Поскольку синхронизация была очень важна, я собрал много статистики по времени прерывания (используя материал QueryPerformanceCounter), чтобы увидеть, насколько на самом деле была плохая ситуация, и был потрясен полученными распределениями. У меня нет удобной статистики, но в основном Windows обслуживала прерывание всякий раз, когда ему казалось, что он работает с нормальным приоритетом. Гистограммы были очень грязными, с stdev шире, чем мой период ~ 3 мс. Часто при обработке прерываний у меня бывают гигантские промежутки в 200 мс или более (напомним, что прерывание срабатывает примерно каждые 3 мс) !! Т.е.: прерывания HW от FAR от точного! Вы застряли на том, что ОС решает сделать для вас.

Однако - когда я обнаружил настройку REALTIME_PRIORITY_CLASS и сравнил ее с этим приоритетом, он был значительно лучше, а распределение интервалов обслуживания было очень узким. Я мог запустить 10 минут 300 кадров в секунду и не пропустить ни одного кадра. Измеренные периоды обслуживания прерываний были примерно равны 1/300 с с жестким распределением.

Кроме того - постарайтесь свести к минимуму другие действия, которые делает ОС, чтобы улучшить шансы вашего времени, работая лучше в приложении, где это важно. Например: нет перекодировки фонового видео или фрагментирования диска или чего-либо еще, пока вы пытаетесь получить точную синхронизацию с другим кодом !!

В итоге:

  1. Если вам это действительно нужно, используйте ОС реального времени
  2. Если вы не можете использовать операционную систему реального времени (невозможную или непрактичную), повышение приоритета вашего процесса, вероятно, значительно улучшит вашу синхронизацию, как это было для меня
  3. Прерывания HW этого не сделают ... ОС по-прежнему должна принять решение об их обслуживании!
  4. Убедитесь, что у вас не запущено много других процессов, конкурирующих за внимание ОС
  5. Если время действительно важно для вас, проведите некоторое тестирование. Хотя заставить код для выполнения точно в нужный момент не очень легко, измерить это отклонение довольно просто. Высокопроизводительные счетчики на ПК (которые вы получаете с QueryPerformanceCounter) чрезвычайно хороши.

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

CHiResTimer.h

#pragma once
#include "stdafx.h"
#include <windows.h>

class CHiResTimer
{
private:
    LARGE_INTEGER frequency;
    LARGE_INTEGER startCounts;
    double ConvertCountsToSeconds(LONGLONG Counts);
public:
    CHiResTimer(); // constructor
    void ResetTimer(void);
    double GetElapsedTime_s(void);
};

CHiResTimer.cpp

#include "stdafx.h"
#include "CHiResTimer.h"

double CHiResTimer::ConvertCountsToSeconds(LONGLONG Counts)
{
    return ((double)Counts / (double)frequency.QuadPart) ;
}

CHiResTimer::CHiResTimer()
{
    QueryPerformanceFrequency(&frequency);
    QueryPerformanceCounter(&startCounts); // starts the timer right away
}

void CHiResTimer::ResetTimer()
{
    QueryPerformanceCounter(&startCounts); // reset the reference counter
}

double CHiResTimer::GetElapsedTime_s()
{
    LARGE_INTEGER countsNow;
    QueryPerformanceCounter(&countsNow);
    return ConvertCountsToSeconds(countsNow.QuadPart - startCounts.QuadPart);
}
5 голосов
/ 06 марта 2011

номер

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

Каждый поток получает временной интервал из операционной системы. Операционная система контролирует порядок запуска потоков.

Когда вы переводите поток в спящий режим, ОС помещает поток в список ожидания, а когда таймер заканчивается, операционная система «будит» поток.
Это означает, что поток добавляется обратно в список активных потоков, но не гарантируется, что t будет добавлен в первую очередь. (Что, если в эту конкретную секунду нужно пробудить 100 потоков? Кто пойдет первым?)

4 голосов
/ 06 марта 2011

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

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

Будьте осторожны при использовании запланированных задач в реальном времени;поскольку они всегда имеют приоритет над стандартными задачами, вы можете легко запрограммировать бесконечный цикл, который никогда не освобождает ЦП и не позволяет администраторам использовать ssh для остановки процесса.Поэтому запускать sshd с более высоким приоритетом в реальном времени может быть не вредно, по крайней мере до тех пор, пока вы не будете уверены, что исправили худшие ошибки.

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

2 голосов
/ 06 марта 2011

Как говорили предыдущие авторы: нет точного способа (некоторые предлагают прерывания в реальном времени или аппаратные прерывания, и даже они не точны ). Я думаю, что вы ищете нечто более точное, чем функция sleep (), и вы обнаружите, что это зависит от вашей ОС, например, функция Windows Sleep () или под GNU функция nanosleep ().

http://msdn.microsoft.com/en-us/library/ms686298%28VS.85%29.aspx

http://www.delorie.com/gnu/docs/glibc/libc_445.html

И то, и другое даст вам точность в течение нескольких миллисекунд.

2 голосов
/ 06 марта 2011

Что ж, вы пытаетесь решить сложную проблему, и достижение точного времени невозможно: лучшее, что вы можете сделать , это использовать аппаратные прерывания , и реализация будет зависеть как от вашего базового оборудования, так и от вашей операционной системы (а именно, вам потребуется операционная система реального времени , которой не является большинство обычных настольных ОС). Какая у вас целевая платформа?

0 голосов
/ 06 марта 2011

Невозможно спать в течение определенного периода времени, используя стандартный C. Вам потребуется как минимум сторонняя библиотека, которая обеспечивает большую детализацию, и вам также может понадобиться специальное ядро ​​операционной системы, такое как в режиме реального времени Ядра Linux.

Например, вот обсуждение того, насколько близко вы можете подойти к системам Win32 .

Это не вопрос C.

0 голосов
/ 06 марта 2011

Нет. Потому что вы всегда зависите от операционной системы, чтобы обрабатывать потоки в нужное время.

...