Boost asyn c таймер не сбрасывается и постоянно вызывается - PullRequest
0 голосов
/ 06 мая 2020

У меня есть таймер в моей программе, который должен подавать сигнал тревоги при двух условиях: 1. Достигнут размер буфера ИЛИ 2. Истекает время таймера.

Я написал свою функцию так, что в конце буфера , он вызывает:

timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));

, из которых, по моему мнению, я ожидал, что вызов async_wait сбросит таймер.

Вместо этого происходит следующее: как только таймер запускается, он вызывается каждые interval (2 секунды) и запускает будильник, который также сбрасывает таймер. По сути, я хочу, чтобы будильник вызывался ТОЛЬКО, ЕСЛИ таймер истек, потому что сейчас, похоже, происходит то, что будильник по таймеру вызывается каждый интервал, а не истечение срока.

* 1010 сделать это (если временной интервал = 2):
t=1 buffer got value...
    timer reset
t=2 buffer got value...
    timer reset
t=3 no value...
    timer = 1 sec
t=4 no value
    timer = 2 sec
    trigger alarm
    timer reset

, но что происходит:

t=1 buffer got value...
    trigger alarm
    timer reset
t=2 buffer got value...
    trigger alarm
    timer reset
t=3 no value...
    trigger alarm
    timer reset
t=4 no value
    timer = 2 sec
    trigger alarm
    timer reset

CODE

cpp

boost::shared_ptr<Database> Database::dbInstance;

Database::Database()
    : work(new io_service::work(io)), interval(2000), timer(io, interval) /* {}; */
{
    // std::cout << " CONSTRUCTED " << std::endl;
}

void Database::setAlarm(const boost::system::error_code& ec)
{
    std::cout << "[TEST] WE ARE IN SET ALARM - ec message = " << ec.message() << std::endl;
    std::cout << "[TEST] WE ARE IN SET ALARM - ec value   = " << ec.value() << std::endl;
    std::cout << "[TEST] WE ARE IN SET ALARM - ec         = " << ec << std::endl;

    DB_WRITE_TIME = 500; // 500 ms

    if(!ec) // success = 0, if !false
    {
        std::cout << " TIMER RESET " << std::endl;
        boost::posix_time::milliseconds interval(DB_WRITE_TIME);
        // timer.expires_at(timer.expires_at() + interval);
        timer.expires_from_now(interval);
        timer.async_wait(boost::bind(&Database::setAlarm, this, _1)); // RESET HERE
    }
}

int Database::buffer()
{
    // do buffer stuff

    // timer.expires_at(timer.expires_at() + interval);
    timer.expires_from_now(interval);
    timer.async_wait(boost::bind(&Database::setAlarm, this, _1)); // RESET HERE
    return 0;
}

заголовок

class Database
{
private:
    static boost::shared_ptr<Database> dbInstance;

public:
    typedef boost::asio::io_service io_service;
    io_service io;
    boost::scoped_ptr<io_service::work> work;
    boost::posix_time::milliseconds interval;
    boost::asio::deadline_timer timer;
    boost::thread timerThread;

    void run_io()
    {
        std::cout << "ENTER IO THREAD" << std::endl;
        io.run();
        std::cout << "LEAVE IO THREAD" << std::endl;
    }

public:
    static Database &getInstance()
    {
        if (!dbInstance)
        {
            std::cout << " INSTANCE CREATED " << std::endl;
            dbInstance.reset(new Database());
            dbInstance->timer.async_wait(boost::bind(&Database::setAlarm, dbInstance.get(), _1));
            dbInstance->timerThread = boost::thread(boost::bind(&Database::run_io,dbInstance));
        }
        return *dbInstance;
    }
};

1 Ответ

0 голосов
/ 06 мая 2020

У вас бесконечное l oop, потому что каждый раз в обработчике завершения вы инициируете новую задачу asyn c:

void handler(err) {     <--------
    if (!err) {                 | handler calls handler
        async_wait(handler);  ---
    }
}

что с этим неясно? Теперь ваше приложение работает нормально, оно работает так, как вы его написали.

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

void alarmDone(const boost::system::error_code& err) {
    if (!err) {
        // no pending tasks were cancelled 
        // interval elapsed, timer expired, alarm is done 
        std::cout << "Done, alarm triggered" << std::endl;

        // Dont initate here any new tasks 
    }
    else {
        // previous pending handler for [timer] cancelled 
    }
}

void setAlaram() {
    boost::posix_time::milliseconds interval(2000);

    timer.expires_from_now(interval);
    timer.async_wait( std::bind(&Database::alarmDone,this,std::placeholders::_1) );
}

Вы можете использовать один setAlarm в качестве рутины для планирования запуска сигнала тревоги и обработки, когда он закончится. Но тогда вам нужно добавить некоторые переменные, чтобы контролировать, когда можно запускать новую задачу:

void Database::setAlarm(const boost::system::error_code& ec)
{
    if(!ec) // success = 0, if !false
    {
        if (alaramStarted) {
            // ok alarm was fired, is done, don't start new async task 
            return;              // abort 
        }
        timer.expires_from_now(interval);
        timer.async_wait(boost::bind(&Database::setAlarm, this, _1)); // RESET HERE
        alarmStarted = true;
    }
}
...