Лучший способ сделать таймер в UNIX
Я всегда замечаю, что у людей разные реакции на создание таймера в UNIX.
Я знаю несколько разных способов, которыми я мог бы сделать событие, выполняемое каждые X секунд в UNIX:
- вы можете сделать поток опроса - pthread_t со сном;
- вы можете сделать select() с таймаутом;
- и я думаю, вы можете использовать ОС несколькими другими способами.
Может ли кто-нибудь предоставить примерный код "наилучшим" или наиболее эффективным способом сделать таймер и описание того, почему он лучший? Я бы хотел использовать наиболее эффективный механизм для этого, но я не уверен, какой он есть!
Для наших целей просто притворись, что печатаешь "Hello World!" раз в 10 секунд.
ПРИМЕЧАНИЕ. У меня нет TR1/Boost/etc. в этой системе, поэтому, пожалуйста, держите его в строгих системных вызовах C/С++ и UNIX. Извините, что не упоминал об этом в первый раз:)
Ответы
Ответ 1
Это зависит от того, что вы хотите сделать. Простой сон будет достаточным для вашего тривиального примера ожидания 10 секунд между "Hello", так как вы можете также приостановить текущий поток до тех пор, пока ваше время не закончится.
Все становится сложнее, если ваш поток действительно что-то делает, пока вы ждёте. Если вы отвечаете на входящие подключения, вы уже будете использовать select
, в таком случае тайм-аут вашего оператора select
наиболее удобен для вашего домашнего хозяйства.
Если вы обрабатываете материал в узком цикле, вы можете регулярно опросить время начала, чтобы узнать, были ли ваши 10 секунд.
alarm
с соответствующим обработчиком сигнала будет работать, но есть серьезные ограничения на то, что вы можете сделать в обработчике сигналов. В большинстве случаев это связано с установкой флага, который нужно будет опросить так часто.
Вкратце, дело сводится к тому, как ваш поток обрабатывает события.
Ответ 2
Используйте управляемый событиями цикл, например Boost.Asio
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
class Timer
{
public:
Timer(
boost::asio::io_service& io_service
) :
_impl( io_service )
{
this->wait();
}
private:
void wait()
{
_impl.expires_from_now( boost::posix_time::seconds(10) );
_impl.async_wait(
boost::bind(
&Timer::handler,
this,
_1
)
);
}
void handler(
const boost::system::error_code& error
)
{
if ( error ) {
std::cerr << "could not wait: " << boost::system::system_error(error).what() << std::endl;
return;
}
std::cout << "Hello World!" << std::endl;
this->wait();
}
private:
boost::asio::deadline_timer _impl;
};
int main()
{
boost::asio::io_service io;
Timer t( io );
io.run();
return 0;
}
построить и запустить:
stackoverflow samm$ ./a.out
Hello World!
Hello World!
Hello World!
^C
stackoverflow samm$
Ответ 3
Если ваша программа уже потоковая, то использование потока опроса является простым и эффективным.
Если ваша программа еще не прошита, тогда становится сложнее. Можете ли вы использовать отдельный процесс для выполнения этой работы? Если это так, то использование многопроцессорной обработки вместо многопоточности.
Если вы не можете использовать автономный поток управления (процесс или поток), это зависит от того, как организована ваша программа, поэтому существует множество предпочтительных альтернатив (они предпочтительны для разных обстоятельств). Это также зависит от точности времени, которое вам требуется. При многосекундных зазорах и отсутствии жестких требований к действию в реальном времени вы можете использовать аварийный сигнал, и обработчик SIGALRM может установить флаг, и ваш основной цикл обработки может контролировать флаг в удобных, разумных точках и выполнять свою деятельность. Это немного грязно (потому что сигналы грязные), но без более подробного описания ваших требований и ограничений трудно понять, что еще предложить.
Итак, в общем, нет единого универсально лучшего решения, потому что разные программы написаны в разных стилях и под разными ограничениями.
Ответ 4
Выбор того, как делать таймер, зависит от простоты использования, мобильности, точности, уродства (глобального состояния/сигналов) и пригодности для приложений реального времени. Лично для наилучшего из всех миров я бы рекомендовал перевернуть свой таймер с помощью потока и использовать clock_nanosleep
с флагом TIMER_ABSTIME
и источником времени CLOCK_MONOTONIC
. Таким образом, вы можете избежать накопленной ошибки и прерываний от установки системного времени, и вы можете рассчитывать перерасход самостоятельно. Теоретически POSIX timer_create
с доставкой SIGEV_THREAD
должен давать вам те же свойства, но реализация glibc действительно плохая и не может гарантировать, что она не потеряет время истечения времени ожидания таймера при большой нагрузке.
Ответ 5
sleep() - самый простой способ. usleep() существует в некоторых вариантах. Если вам нужна точность в секундах, выберите (NULL, NULL, NULL & timeout) наиболее портативный, если вы не напишите свои собственные подпрограммы, специфичные для ОС. Метод select() имеет то преимущество, что тайм-аут обновляется, чтобы отразить количество оставшегося времени, если вызов был прерван сигналом. Вы можете возобновить ожидание, если это необходимо.
Действительно, все методы эквивалентны; OS перестает планировать поток для периода ожидания.
Метод SIGALARM, предложенный Blagovestus, (несколько) неспособен, хотя он должен работать на Linux.
Ответ 6
Это поздний ответ, но я хотел добавить свою реализацию таймера. Это поточный таймер и требует некоторой работы, с обновением мьютексов и pthreads в своих классах (отсутствие RAII в этом отношении). Это также не перекрестная платформа, так как она использует метод gettimeofday()
.
В коде используется таймер, который вызывает объект CallBack. Весь источник можно увидеть на
http://matthewh.me/playground/browser/branches/C%2B%2B/c%2B%2B_callbacks
Ответ 7
Если вам просто нужно вывести "Hello world" один раз каждые 10 секунд, я думаю, что будут спать, и наносы будут делать. Если ваши задачи как-то более важны, вы можете взглянуть на RealTime Linux. Там
(www.ee.nmt.edu/~rison/ee352_fall09/Getting_Started_with_RTLinux.pdf) они положили хорошее руководство по этому поводу. При использовании RTLinux вы можете использовать pthread_make_periodic_np
для периодического выполнения задачи, хотя вы будете отвечать за то, чтобы убедиться, что условия в реальном времени выполнены.