I'm trying to write a simple CLI app that needs to send data to another machine every 8 milliseconds (125hz processing). I have my TCP/IP connection established, and I figured I could just create a processing loop, but how can I make the loop process at 8ms? Mind you, not the time that it takes to calculate the data it's sending plus 8ms...just 8ms. Kinda like a timer event that triggers every 8ms. Any thoughts or links?
I already have a little experience using it for UDP so I figured it'd be an easy start for me.
I haven't even started forming a loop for this yet, I've started the initialization and handshake stuff. It will all be running on a lan with minimal other traffic, so timing should be fine. I'd love to see what you have either way.
The thing is, I don't want to "wait" really, there will be calculations and such taking place...I think I need some kind of callback or something that's triggered by an 8ms timer...
I need the event to be triggered every 8ms, and if every calculation takes less than 8ms to calculate, then even though there's an offset from the time its called, at least the sending will be consistently 8ms...I have no idea how to even start building this sort of loop. All of my C/C++ experience is in writing plugins for software that already has loops and framerates defined.
SamuelAdams, you were going to share some code?
Also, keep in mind, this will not be going out over the internet, it's for a local area network between two machines in the same room.
I'll read up on the chrono library...but if I can see some good examples, that'd be awesome.
#include <iostream>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <ctime>
// call the function periodically, once every 'period_in_milliseconds' milliseconds.
// repeat the call a maximum of 'ntimes' times or till 'stop_calls' is set to true
void periodic_call( int period_in_milliseconds, int ntimes, std::atomic<bool>& stop_calls, std::function< void() > function )
{
constauto period = std::chrono::milliseconds(period_in_milliseconds) ;
for( int i = 0 ; i < ntimes && !stop_calls ; ++i )
{
auto start = std::chrono::steady_clock::now() ;
function() ; // assumes that the function won't take more than 'period_in_milliseconds' milliseconds
auto end = std::chrono::steady_clock::now() ;
// set delta to the time (in milliseconds) that was used to execute the function
constauto delta = std::chrono::duration_cast<std::chrono::milliseconds>(end-start) ;
// sleep till the wait time has elapsed
std::this_thread::sleep_for( period - delta ) ;
}
}
void foo( int, int )
{
staticint n = 0 ;
auto t = std::time(nullptr) ;
char now[128] ;
std::strftime( now, sizeof(now), "foo called at %H:%M:%S\n", std::localtime(&t) ) ;
std::cout << ++n << ". " << now << std::flush ;
}
int main()
{
// launch a thread to call foo(2,3) once every 2 seconds, max 500 times in all
// note: stop_calls is passed as a wrapped reference to std::atomic<bool>
std::atomic<bool> stop_calls { false } ;
std::thread call_back_thread( periodic_call, 2000, 500, std::ref(stop_calls), std::bind( foo, 2, 3 ) ) ;
std::this_thread::sleep_for( std::chrono::seconds(10) ) ; // sleep for 10 seconds
stop_calls = true ; // stop the calls
call_back_thread.join() ; // wait for the thread to finish
std::cout << "done!\n" ;
}
To get a more precise estimate of the duration in milliseconds, use a duration with a floating point type as the representation and milliseconds as the period.
If output streaming of objects of type std::chrono::duration is a frequent requirement, overloading the stream insertion operator would be convenient. For instance:
For production code, use Hinnant's non-viral (MIT license) date and time library which extends the facilities in the standard C++ library <chrono>https://github.com/HowardHinnant/date
Super helpful dude. I appreciate it so much! I don't think this will be much of a repeat function, I'm not sure how much I'll need to use...what I've got is working pretty good...Only thing is, I'm not 100% sure that the thread is stopping when I ask it to. I need to move stop_calls and call_back_thread up in scope so I can stop it from other functions. How do I instantiate call_back_thread globally? thread call_back_thread; doesn't work...does it need the full declaration/constructor?
1 2
stop_calls = true ; // stop the calls
call_back_thread.join() ; // wait for the thread to finish
In your example, the lines above for join on call_back_thread...is that a blocking function? does it actually wait until the thread stops or will the thread continue to fire a few more times after this line runs?
> I need to move stop_calls and call_back_thread up in scope so I can stop it from other functions.
Is only one callback in the entire program, which can be stopped from other functions;
or are there many callbacks, each of which has to be individually started and stopped from different functions?
> the lines above for join on call_back_thread...is that a blocking function?
Yes.
> does it actually wait until the thread stops or will the thread continue to fire a few more times after this line runs?
It will wait until the thread had finished execution.
The simplest modification to the earlier program would be: make the flag stop_calls an object declared at namespace scope (instead of a parameter passed by reference to the periodic_call function). Then it can be set to true from anywhere in the program.
#include <iostream>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <ctime>
extern std::atomic<bool> stop_calls ; // you may want to place this in a header file
// (included by functions which want to stop the calls)
std::atomic<bool> stop_calls { false } ;
// call the function periodically, once every 'period_in_milliseconds' milliseconds.
// repeat the call a maximum of 'ntimes' times or till 'stop_calls' is set to true
void periodic_call( int period_in_milliseconds, int ntimes, std::function< void() > function )
{
constauto period = std::chrono::milliseconds(period_in_milliseconds) ;
for( int i = 0 ; i < ntimes && !stop_calls ; ++i )
{
auto start = std::chrono::steady_clock::now() ;
function() ; // assumes that the function won't take more than 'period_in_milliseconds' milliseconds
auto end = std::chrono::steady_clock::now() ;
// set delta to the time (in milliseconds) that was used to execute the function
const std::chrono::duration< double, std::ratio<1,1000> > delta(end-start) ;
// sleep till the wait time has elapsed
if( !stop_calls ) std::this_thread::sleep_for(period-delta) ;
}
}
void foo( int, int )
{
staticint n = 0 ;
auto t = std::time(nullptr) ;
char now[128] ;
std::strftime( now, sizeof(now), "foo called at %H:%M:%S\n", std::localtime(&t) ) ;
std::cout << ++n << ". " << now << std::flush ;
}
void some_fun() ;
int main()
{
// launch a thread to call foo(2,3) once every second, max 500 times in all
std::thread call_back_thread( periodic_call, 1000, 500, std::bind( foo, 2, 3 ) ) ;
some_fun() ;
call_back_thread.join() ; // wait for the thread to finish
std::cout << "done!\n" ;
}
void some_fun()
{
for( int i = 0 ; i < 16 ; ++i )
{
std::cout << "waiting...\n" << std::flush ;
std::this_thread::sleep_for( std::chrono::milliseconds(400) ) ;
}
std::cout << "\nstopping the calls\n" << std::flush ;
stop_calls = true ; // this may be done from any function
}