Question

I am trying to write a simple program that within its main loop calls different functions based on user input (no problem here) and to also perform some actions on its own - those are time based. Since I want to avoid potential data access issues (I'm not good at multithreading) I was trying to use callbacks to ensure various autonomous parts of the program would be called every so-and-so milliseconds.

From what I've found, boost::asio looks like the way to go - however I don't know what is the correct approach here. I've made a simple code that works:

#include <iostream>

#include <boost/asio.hpp>//scheduling
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;

void clb(const boost::system::error_code&){
    //  eye.procFrame(&img, true);
    cout << "callback ok" << endl;
}
int main() {
    bool run = true;
    int i =0;

    while(run){
        boost::asio::io_service io;
        boost::asio::deadline_timer t(io, boost::posix_time::seconds(2));
        t.async_wait(clb);
        io.run();
        if(++i>10) run = false;
    }
    cout << "done, i=" << i;
    return 0;
}

My primary concern is the declarations - if I move them outside of the while loop, the callback occurs only once. Is there a way to write callbacks to resemble timer interrupts from microcontrollers?

Sleep is not a solution - it would render the program unresponsive and wouldn't guarantee, that parts of the program are updated according to their schedule (for instance GUI can update every 25ms, but image capture should occur only 10 times a second[100ms period) as a heavy operation taking a lot of resources]

Perhaps there is a completely different approach that would be better? I was thinking about more threads, but I fear if I try and thread every piece of program I'll end up with a bowl of spaghetti I'd never be able to manage on the long run.

Was it helpful?

Solution

If you want your timer to fire repeatedly, you can just call deadline_timer::expires_from_now and deadline_timer::async_wait in your timer callback. This will make the timer re-schedule itself each time it fires.

The timers are associated with an io_service object, which should be run on one or more threads. You can have any number of timers associated with a single io_service. If you want your timers to be executed serially and not worry about concurrency issues, just have a single thread run your io_service.

In this example, I have one thread running one io_service with two timers.

#include <iostream>
#include <boost/asio.hpp>
#include <boost/chrono.hpp>

boost::asio::io_service io;
boost::asio::deadline_timer timerA(io);
boost::asio::deadline_timer timerB(io);
boost::chrono::steady_clock::time_point appStart;

void onTimerA(const boost::system::error_code& error)
{
    assert(!error);
    std::cout << "A fired at " << 
        boost::chrono::duration_cast<boost::chrono::milliseconds>
        (boost::chrono::steady_clock::now() - appStart).count() 
        << std::endl;

    timerA.expires_from_now(boost::posix_time::seconds(1));
    timerA.async_wait(onTimerA);
}

void onTimerB(const boost::system::error_code& error)
{
    assert(!error);
    std::cout << "B fired at " << 
        boost::chrono::duration_cast<boost::chrono::milliseconds>
        (boost::chrono::steady_clock::now() - appStart).count() 
        << std::endl;

    timerB.expires_from_now(boost::posix_time::seconds(2));
    timerB.async_wait(onTimerB);
}

int main()
{
     // Prevents io.run() from returning.
    boost::asio::io_service::work work(io);

    appStart = boost::chrono::steady_clock::now();

    timerA.expires_from_now(boost::posix_time::seconds(1));
    timerA.async_wait(onTimerA);

    timerB.expires_from_now(boost::posix_time::seconds(2));
    timerB.async_wait(onTimerB);

    io.run();
}

However, the downside of having one thread is that if one of the timer callbacks takes a long time to execute, it will block other timers that should have fired until it completes. If you know that your timer callbacks won't take long, one thread will be fine, though.

For example, if you have timer A on 25 ms intervals and timer B on 50 ms intervals, the two timers will sometimes be scheduled to fire at the same time. One of them will execute first, the other will wait for that execution to complete, and then execute itself.

If you do expect your timer callbacks to take a long time and you can't have your timers waiting on other timers' callbacks to finish, you need additional threads running your io_service.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top