使用 boost::asio 实现事件计时器

Implementing an event timer using boost::asio

本文关键字:事件 计时器 实现 asio boost 使用      更新时间:2023-10-16

示例代码看起来很长,但实际上它并不那么复杂:-)

我想做的是,当用户调用 EventTimer.Start() 时,它将每 interval 毫秒执行一次回调处理程序(传入 ctor),持续 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. 我应该在事件计时器::停止() 为了重置一切都是为了下次调用 Start() 会起作用吗?

  2. 还有什么需要修改的吗?

  3. 如果我使用另一个线程来启动 EventTimer::Start()(请参阅主函数中的注释代码),线程何时实际退出?

谢谢。

彼得

正如 Sam 所暗示的那样,根据你试图完成的任务,大多数时候停止io_service被认为是设计错误。您无需stop()/reset() io_service即可重新安排计时器。

通常,您会将线程或线程池附加到io_service,然后您将使用io_service安排所需的任何事件。io_service机器就位后,由io_service根据请求调度您的计划工作,然后您只需处理您与io_service一起安排的事件或工作请求。

我并不完全清楚您要完成什么,但是您发布的代码中有一些不正确的地方。

  1. io_service::reset() 只有在文档所述io_service::run()的先前调用停止或工作耗尽后才应调用。
  2. 你不应该需要显式调用Sleep(),只要它有工作要做,对io_service::run()的调用就会阻塞。

我想通了,但我不知道为什么我必须把io.reset()放在Start()中,因为它已经在 Stop() 中调用了。

请参阅帖子中的更新代码。