Реализация таймера событий с использованием boost :: asio - PullRequest
1 голос
/ 20 мая 2011

Пример кода выглядит длинным, но на самом деле все не так сложно: -)

Что я пытаюсь сделать, так это то, что когда пользователь вызывает EventTimer.Start (), он будет выполнять обработчик обратного вызова (который передается в ctor) каждые interval миллисекунды для repeatCount раз.

Вам просто нужно взглянуть на функцию EventTimer :: Stop ()

#include <iostream>
#include <string>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <ctime>
#include <sys/timeb.h>
#include <Windows.h>

std::string CurrentDateTimeTimestampMilliseconds() {
    double ms = 0.0;            // Milliseconds

    struct timeb curtime;
    ftime(&curtime);
    ms = (double) (curtime.millitm);

    char timestamp[128];

    time_t now = time(NULL);
    struct tm *tp = localtime(&now);
    sprintf(timestamp, "%04d%02d%02d-%02d%02d%02d.%03.0f",
            tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, ms);

    return std::string(timestamp);
}

class EventTimer
{
public:
    static const int kDefaultInterval     = 1000;
    static const int kMinInterval         = 1;
    static const int kDefaultRepeatCount  = 1;
    static const int kInfiniteRepeatCount = -1;
    static const int kDefaultOffset       = 10;

public:
    typedef boost::function<void()> Handler;

    EventTimer(Handler handler = NULL)
        : interval(kDefaultInterval),
          repeatCount(kDefaultRepeatCount),
          handler(handler),
          timer(io),
          exeCount(-1)
    {

    }

    virtual ~EventTimer()
    {

    }

    void SetInterval(int value)
    {
//        if (value < 1)
//            throw std::exception();

        interval = value;
    }

    void SetRepeatCount(int value)
    {
//        if (value < 1)
//            throw std::exception();

        repeatCount = value;
    }

    bool Running() const
    {
        return exeCount >= 0;
    }

    void Start()
    {
        io.reset(); // I don't know why I have to put io.reset here,
                    // since it's already been called in Stop()
        exeCount = 0;

        timer.expires_from_now(boost::posix_time::milliseconds(interval));
        timer.async_wait(boost::bind(&EventTimer::EventHandler, this));
        io.run();
    }

    void Stop()
    {
        if (Running())
        {
            // How to reset everything when stop is called???
            //io.stop();
            timer.cancel();
            io.reset();

            exeCount = -1; // Reset
        }
    }

private:
    virtual void EventHandler()
    {
        // Execute the requested operation
        //if (handler != NULL)
        //    handler();
        std::cout << CurrentDateTimeTimestampMilliseconds() << ": exeCount = " << exeCount + 1 << std::endl;

        // Check if one more time of handler execution is required
        if (repeatCount == kInfiniteRepeatCount || ++exeCount < repeatCount)
        {
            timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(interval));
            timer.async_wait(boost::bind(&EventTimer::EventHandler, this));
        }
        else
        {
            Stop();
            std::cout << CurrentDateTimeTimestampMilliseconds() << ": Stopped" << std::endl;
        }
    }

private:
    int                         interval;    // Milliseconds
    int                         repeatCount; // Number of times to trigger the EventHandler
    int                         exeCount;    // Number of executed times
    boost::asio::io_service     io;
    boost::asio::deadline_timer timer;
    Handler                     handler;
};

int main()
{
    EventTimer etimer;

    etimer.SetInterval(1000);
    etimer.SetRepeatCount(1);

    std::cout << CurrentDateTimeTimestampMilliseconds() << ": Started" << std::endl;
    etimer.Start();
    // boost::thread thrd1(boost::bind(&EventTimer::Start, &etimer));

    Sleep(3000); // Keep the main thread active

    etimer.SetInterval(2000);
    etimer.SetRepeatCount(1);

    std::cout << CurrentDateTimeTimestampMilliseconds() << ": Started again" << std::endl;
    etimer.Start();
    // boost::thread thrd2(boost::bind(&EventTimer::Start, &etimer));

    Sleep(5000); // Keep the main thread active
}

/* Current Output:
20110520-125506.781: Started
20110520-125507.781: exeCount = 1
20110520-125507.781: Stopped
20110520-125510.781: Started again
 */

/* Expected Output (timestamp might be slightly different with some offset)
20110520-125506.781: Started
20110520-125507.781: exeCount = 1
20110520-125507.781: Stopped
20110520-125510.781: Started again
20110520-125512.781: exeCount = 1
20110520-125512.781: Stopped
 */

Я не знаю, почему мой второй раз при вызове EventTimer :: Start () не работает вообще. Мои вопросы:

  1. Что мне делать в EventTimer :: Stop () для сброса все так, чтобы в следующий раз вызов Start () будет работать?

  2. Что-нибудь еще, что я должен изменить?

  3. Если я использую другой поток для запуска EventTimer :: Start () (см. Прокомментированный код в основной функции), когда поток действительно завершится?

Спасибо.

Peter

Ответы [ 3 ]

2 голосов
/ 22 мая 2011

Как Сэм намекнул, в зависимости от того, что вы пытаетесь выполнить, большую часть времени считается ошибкой проектирования, чтобы остановить io_service.Вам не нужно stop() / reset() io_service для того, чтобы перепланировать таймер.

Обычно вы оставляете поток или пул потоков, работающий в привязке к io_service, и затем вы планируете любое событиевам нужно с io_service.С установленным механизмом io_service оставьте его до io_service для отправки запланированной работы в соответствии с запросом, и тогда вам нужно будет работать только с событиями или рабочими запросами, которые вы запланировали с помощью io_service.

1 голос
/ 21 мая 2011

Мне не совсем ясно, что вы пытаетесь выполнить, но есть пара вещей, которые неверны в коде, который вы опубликовали.

  1. io_service::reset() должен вызываться только послепредыдущий вызов io_service::run() был остановлен или закончился без работы, поскольку документация описывает .
  2. , вам не нужно явно вызывать Sleep(), вызов io_service::run() будет блокироваться какПока у него есть работа.
0 голосов
/ 21 мая 2011

Я понял это, но я не знаю, почему я должен поместить io.reset() в Start(), так как он уже был вызван в Stop ().

Смотрите обновленный код в посте.

...