Executing a function at specific intervals
Solution 1
Why do you need a timer for this?
You could just measure the execution time and take a sleep according to the relation of elapsed time to desired interval duration.
Example:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main() {
srand(1);
for (;;) {
double interval = 10; /* seconds */
/* start time */
time_t start = time(NULL);
/* do something */
int duration = rand() % 13;
printf("%2d seconds of work started at %s", duration, ctime(&start));
sleep(duration);
/* end time */
time_t end = time(NULL);
/* compute remaining time to sleep and sleep */
double elapsed = difftime(end, start);
int seconds_to_sleep = (int)(interval - elapsed);
if (seconds_to_sleep > 0) { /* don't sleep if we're already late */
sleep(seconds_to_sleep);
}
}
return 0;
}
Output:
$ gcc test.c && ./a.out
0 seconds of work started at Sun Mar 17 21:20:28 2013
9 seconds of work started at Sun Mar 17 21:20:38 2013
11 seconds of work started at Sun Mar 17 21:20:48 2013
4 seconds of work started at Sun Mar 17 21:20:59 2013
1 seconds of work started at Sun Mar 17 21:21:09 2013
^C
Solution 2
I had a nearly identical use case except I needed it to be cross platform C++11 and needed to do other tasks in the meantime rather than sleeping. Here is my code in case it is useful to someone:
// TimeT.h - Calls the passed function if the minimum time interval has elapsed
#ifndef TIME_T_H_
#define TIME_T_H_
#include <chrono>
/// Tracks the time since execution() was called, and only
/// Calls the passed function if the minimum time interval has elapsed
/// @see https://stackoverflow.com/questions/2808398/easily-measure-elapsed-time for the code I based this on
template<typename TimeT = std::chrono::milliseconds>
struct periodic
{
periodic(TimeT duration = TimeT(1))
: start(std::chrono::system_clock::now())
, period_duration(duration)
, previous_duration(TimeT::zero())
{};
template<typename F, typename ...Args>
TimeT execution(F func, Args&&... args)
{
auto duration = std::chrono::duration_cast<TimeT>
(std::chrono::system_clock::now() - start);
if (duration > previous_duration + period_duration)
{
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
previous_duration = duration;
}
return duration;
}
std::chrono::time_point<std::chrono::system_clock> start;
// The minimum duration to wait before the function can be called again
TimeT period_duration;
// The duration between startup and the last time the function was called
TimeT previous_duration;
};
#endif // TIME_T_H_
Here is an example of using it:
#include "TimeT.h"
#include <iostream>
#include <thread>
int main(int argc, char* argv[])
{
periodic<> callIfMinPeriodPassed(std::chrono::milliseconds(1));
std::size_t num_periods;
while(true)
{
callIfMinPeriodPassed.execution( [&num_periods]()
{
std::cout << ++num_periods << "timesteps have passed\n"
});
// do other stuff here, this example will work
// but spins at 100% CPU without the sleep
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
This implementation is based on a modification of this stackoverflow question about measuring elapsed time.
Related videos on Youtube
m4n07
Updated on October 08, 2022Comments
-
m4n07 over 1 year
The task is to execute a function (say
Processfunction()
) every x (say x=10) seconds.With below code, I'm able to call
Processfunction()
every x seconds.Question: How to handle the case where the function takes more than 10 seconds to finish execution?
One way would be to have a flag to indicate the end of
Processfunction()
execution and check for it before callingProcessfunction()
. Is there a better way to do this ?
#include <pthread.h> #include <unistd.h> // for sleep() and usleep() void *timerthread(void *timer_parms) { struct itimerspec new_value; int max_exp, fd; struct timespec now; uint64_t exp; ssize_t s; struct timer_params *p =(struct timer_params*)timer_parms; printf("starttimer Start\n"); /* Create a CLOCK_REALTIME absolute timer with initial expiration and interval as specified in command line */ if (clock_gettime(CLOCK_REALTIME, &now) == -1) handle_error("clock_gettime"); new_value.it_value.tv_sec = now.tv_sec; new_value.it_value.tv_nsec = now.tv_nsec + p->tv_nsec; new_value.it_interval.tv_sec = p->tv_sec; new_value.it_interval.tv_nsec = p->tv_nsec; //max_exp = 5; //No of times fd = timerfd_create( CLOCK_REALTIME , 0); if (fd == -1) handle_error("timerfd_create"); if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1) handle_error("timerfd_settime"); printf("timer started\n"); while(1) // keep checking { s = read(fd, &exp, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("read"); Processfunction(); // Say after X seconds call this function } return NULL; } int main() { struct timer_params timer_params_obj; int res;void *thread_result; timer_params_obj.tv_sec = 10; //timer_params_obj.tv_nsec = 10000000 ; //10ms timer_params_obj.tv_nsec = 0 ; pthread_t pt; pthread_create(&pt, NULL, timerthread, &timer_params_obj); // thread is running and will call Processfunction() every 10 sec }
-
Martin James about 11 years+1 - yup - that's the way I would do it. Timers are often inappropriately used :(
-
Andrew Hundt almost 9 yearsCan this be done without linux specific code (boost or c++11) and without sleeps? The program has other things to do in the meantime.
-
moooeeeep over 8 years@AndrewHundt You can use Boost.Asio's deadline timers to asynchronously wait for a specific time. You might need to restructure your code a bit due to the asynchronous program flow (if that is not already the case).
-
Andrew Hundt over 8 years@moooeeeep yeah that is a good way to go about it too, I've used them before. However, I wanted to put the code in a very tiny example and I find the typical developer has a tough time grasping asio immediately so I wanted to avoid too much complexity. I put the solution I went with in another answer below in case someone else comes across this that can't use the accepted answer's dependencies.