Каков наилучший способ выйти из цикла по истечении 30 мс на С++?

Каков наилучший способ выйти из цикла как можно ближе к 30 мс в С++. Ускорение опроса: microsec_clock ? Опрос QTime ? Что-то другое?

Что-то типа:

A = now;
for (blah; blah; blah) {
    Blah();
    if (now - A > 30000)
         break;
}

Он должен работать в Linux, OS X и Windows.

Вычисления в цикле предназначены для обновления симуляции. Каждые 30 мс я хотел бы обновить область просмотра.


person Neil G    schedule 03.06.2009    source источник
comment
добавил тег qt к вашему вопросу, как вы упомянули QTime   -  person Idan K    schedule 03.06.2009
comment
Вы имеете в виду любое время после 30 мс или как можно ближе к 30 мс?   -  person Max Lybbert    schedule 03.06.2009
comment
Может быть, это поможет рассказать, чего вы действительно хотите достичь.   -  person lothar    schedule 03.06.2009
comment
Возможно, вы также захотите добавить, какое оборудование/ОС вы используете. Различные аппаратные платформы предоставляют счетчики времени с разной точностью.   -  person J. Polfer    schedule 03.06.2009
comment
Ответил на комментарии в вопросе.   -  person Neil G    schedule 03.06.2009
comment
Спасибо всем за отличные ответы! Для этого существует StackOverflow. Три верхних ответа — это все решения: используйте clock(), используйте потоки и опрашивайте аппаратные часы. Первое решение требует наименьшей работы и является моим временным решением, пока я не разработаю многопоточную модель. (Удивительно, но никто не говорил о boost::microsec_timer или как там это называется.)   -  person Neil G    schedule 04.06.2009


Ответы (9)


Пример фрагмента кода в этой ссылке в значительной степени делает то, что вы хотите:

http://www.cplusplus.com/reference/clibrary/ctime/clock/< /а>

Адаптировано из их примера:

void runwait ( int seconds )
{
   clock_t endwait;
   endwait = clock () + seconds * CLOCKS_PER_SEC ;
   while (clock() < endwait)
   {
      /* Do stuff while waiting */
   }
}
person K M    schedule 03.06.2009
comment
Можно ли использовать библиотеку ctime, чтобы приблизиться к степени детализации (30 мс), о которой он просит? - person J. Polfer; 03.06.2009
comment
-1, это занятое ожидание (процесс будет сосать ЦП, ничего не делая). Случаи, когда занятое ожидание допустимо, весьма специфичны (в этом случае допустимо 30 мс, если это единственное решение, но не целая секунда). - person Bastien Léonard; 03.06.2009
comment
Правда, на графическом фреймворке это ни-ни (угадывая по ссылке Qt). Если это просто консольное приложение, то, вероятно, все в порядке, так как ОС позаботится об этом. - person J. Polfer; 03.06.2009
comment
На моей машине Intel под управлением OS X: #define CLOCKS_PER_SEC (__DARWIN_CLK_TCK) #define __DARWIN_CLK_TCK 100 /* тактов в секунду */ - person Neil G; 03.06.2009
comment
Я не пытаюсь ждать, но опрос clock() будет работать, если CLOCKS_PER_SEC всегда ›= 100 (по крайней мере) - person Neil G; 03.06.2009

Вычисления в цикле предназначены для обновления симуляции. Каждые 30 мс я хотел бы обновить окно просмотра.

Рассматривали ли вы использование потоков? То, что вы описываете, кажется прекрасным примером того, почему вы должны использовать потоки вместо таймеров.

Основной поток процесса продолжает заботиться о пользовательском интерфейсе и имеет QTimer, установленный на 30 мс, для его обновления. Он блокирует QMutex для доступа к данным, выполняет обновление и освобождает мьютекс.

Второй поток (см. QThread) выполняет моделирование. Для каждого цикла он блокирует QMutex, выполняет вычисления и освобождает мьютекс, когда данные находятся в стабильном состоянии (подходит для обновления пользовательского интерфейса).

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

person Juliano    schedule 03.06.2009
comment
Проголосовал. Спасибо, я уделю этому серьезное внимание. Прямо сейчас отладка однопоточного приложения — это проблема, но когда оно станет стабильным, я, вероятно, перейду на многопоточную модель. Спасибо за подробное описание плана преобразования этого приложения в многоядерное. - person Neil G; 03.06.2009
comment
Если бы плакат говорил о реализации контроля за ходом выполнения или чего-то подобного, я бы, вероятно, использовал службу/обратный вызов таймера, который проверял какое-то общее состояние. Мой обычный поток продолжит обработку и обновит какой-то маркер, а поток обратного вызова позаботится об обновлении. Например, в Windows вы поймаете сообщение WM_TIMER. - person Chris K; 04.06.2009
comment
@darthcoder В многопоточных процессах вы не обновляете маркеры, вы должны использовать монитор или мьютекс, иначе вы рискуете коснуться данных, которые не готовы к использованию. Кроме того, вы должны оставить основной поток бездействующим, получая сообщения и выполняя обновления пользовательского интерфейса, и иметь вторичный поток (не в цикле событий), выполняющий тяжелую работу. Таким образом, ваше приложение остается отзывчивым во время обработки. QTimer делает то, что вы предложили с помощью WM_TIMER, переносимым способом (Нил Г. сказал, что он должен быть переносимым). - person Juliano; 04.06.2009

Хотя это не отвечает на вопрос, это может дать другой взгляд на решение. Как насчет размещения кода моделирования и пользовательского интерфейса в разных потоках? Если вы используете Qt, периодическое обновление можно реализовать с помощью таймера или даже QThread::msleep( ). Вы можете адаптировать пример Мандельброта с резьбой в соответствии со своими потребностями.

person Ariya Hidayat    schedule 03.06.2009
comment
Обе ссылки в ответе мертвы. - person Pang; 26.03.2018

Если вам нужно выполнить работу до истечения определенного времени, тогда ответ docflabby точен. Однако, если вам нужно просто подождать, ничего не делая, пока не истечет указанное время, вам следует использовать usleep()

person SingleNegationElimination    schedule 03.06.2009
comment
Мне нужно работать, но спасибо, что сообщили мне о usleep(). - person Neil G; 03.06.2009

Короткий ответ: в общем случае вы не можете, но можете, если работаете на правильной ОС или на правильном оборудовании.

Вы можете получить БЛИЗКО 30 мс на всех ОС, используя вызов сборки в системах Intel и что-то еще в других архитектурах. Я откопаю ссылку и отредактирую ответ, чтобы включить код, когда найду его.

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

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

редактировать: ЛОЛ! Кто-то уже опубликовал аналогичный фрагмент на SO: Функция таймера для предоставления времени в наносекундах с использованием C++

У VonC есть комментарий с ассемблерным кодом процессорного таймера.

person James    schedule 03.06.2009
comment
Я не игнорировал ваш ответ - я изучал его. Кажется, это лучший способ получить разрешение выше 0,01 с. Тем не менее, это намного больше работы для меня. - person Neil G; 03.06.2009
comment
Прямое использование RDTSC — это рецепт боли из-за SMP и управления питанием. Функции синхронизации ОС могут использовать разумный таймер (например, HPET) или пытаться обойти рассогласование часов SMP (например, QPC() с драйвером процессора AMD). - person bk1e; 04.06.2009

Согласно вашему вопросу, каждые 30 мс вы хотите обновлять окно просмотра. Однажды я написал похожее приложение, которое проверяло оборудование каждые 500 мс на наличие похожих вещей. Хотя это не дает прямого ответа на ваш вопрос, у меня есть следующие дополнения:

  • Вы уверены, что Blah() для обновления области просмотра может выполняться менее чем за 30 мс в каждом экземпляре?
  • Больше похоже на то, что запуск Blah() будет лучше выполняться обратным вызовом таймера.
  • Очень сложно найти библиотечный объект таймера, который будет устанавливать интервал в 30 мс для выполнения обновлений в графическом фреймворке. В Windows XP я обнаружил, что стандартный API-таймер Win32, который отправляет оконные сообщения по истечении интервала таймера, даже на 2-ГГц P4, не может выполнять обновления быстрее, чем с интервалом 300 мс, независимо от того, насколько малый интервал времени я установил на таймер. Хотя в Win32 API были доступны высокопроизводительные таймеры, у них есть много ограничений, а именно то, что вы не можете выполнять какие-либо IPC (например, виджеты обновления пользовательского интерфейса) в цикле, подобном тому, который вы указали выше.
  • По сути, в результате вы должны очень тщательно спланировать, как вы хотите, чтобы обновления происходили. Возможно, вам придется использовать потоки и посмотреть, как вы хотите обновить область просмотра.

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

person J. Polfer    schedule 03.06.2009
comment
Спасибо за ваши последующие действия. Blah() — это код обновления моделирования, а не код обновления пользовательского интерфейса. Почти наверняка это не займет больше 30 мс. Если это так, я могу добавить внутрь него код опроса часов, предполагая, что код опроса часов работает быстро. Если атомарные операции внутри Blah() занимают больше 30 мс, то пользователю не повезло. Код обновления пользовательского интерфейса автоматически вызывается QT при выходе из цикла и возврате функции. Функция вызывается при обратном вызове таймера, как вы предлагаете. Я не думаю, что у меня вообще есть IPC, но я могу ошибаться. - person Neil G; 03.06.2009

Вы можете подумать об обновлении окна просмотра каждые N шагов моделирования, а не каждые K миллисекунд. Если это (скажем) серьезное коммерческое приложение, то вы, вероятно, захотите пойти по многопоточному маршруту, предложенному в другом месте, но если (скажем) это для личного использования или использования ограниченной аудиторией и что вас действительно интересует - это детали того, что вы моделируете, тогда каждый-N-шаг прост, портативен и вполне может быть достаточно хорош, чтобы с ним можно было справиться.

person Community    schedule 03.06.2009

См. QueryPerformanceCounter и QueryPerformanceFrequency.

person gatorfax    schedule 03.06.2009
comment
Предполагая, что вы работаете на платформе Win32; OP упомянул Qt, поэтому я предполагаю, что OP не использует/не может использовать Win32 API. - person J. Polfer; 03.06.2009
comment
Да, я бы хотел оставаться независимым от ОС, если это возможно. Если бы я нашел решение для трех упомянутых ОС, я мог бы написать независимый от платформы код опроса времени, но я надеялся, что кто-то уже это сделал. - person Neil G; 03.06.2009
comment
Мой ответ был опубликован ДО того, как вопрос был обновлен, чтобы указать ОС. Спасибо. - person gatorfax; 03.06.2009

Если вы используете Qt, вот простой способ сделать это:

QTimer* t = new QTimer( parent ) ;
t->setInterval( 30 ) ; // in msec
t->setSingleShot( false ) ;
connect( t, SIGNAL( timeout() ), viewPort, SLOT( redraw() ) ) ;

Вам нужно указать viewPort и redraw(). Затем запустите таймер с помощью t->start().

person swongu    schedule 03.06.2009
comment
Я не думаю, что это прервет цикл for, так как все это происходит в одном потоке. - person Neil G; 03.06.2009
comment
Однако я использую QTimer для вызова функции, содержащей цикл for. - person Neil G; 03.06.2009
comment
Почему бы вам просто не использовать цикл событий Qt? Будете ли вы делать что-то еще внутри цикла for? - person swongu; 04.06.2009
comment
мой цикл обновляет симуляцию. Он работает в цикле событий, но периодически должен возвращаться в цикл событий — не слишком быстро и не слишком медленно. Вот почему я спрашиваю. - person Neil G; 04.06.2009