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

发布于 2024-11-08 22:07:35 字数 5108 浏览 3 评论 0原文

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

我想做的是,当用户调用 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()(请参阅主函数中的注释代码),该线程何时实际退出?

谢谢。

彼得

The sample code looks long, but actually it's not so complicated :-)

What I'm trying to do is, when a user calls EventTimer.Start(), it will execute the callback handler (which is passed into the ctor) every interval milliseconds for repeatCount times.

You just need to look at the function 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
 */

I don't know why that my second time of calling to EventTimer::Start() does not work at all. My questions are:

  1. What should I do in
    EventTimer::Stop() in order to reset
    everything so that next time of
    calling Start() will work?

  2. Is there anything else I have to modify?

  3. If I use another thread to start the EventTimer::Start() (see the commented code in the main function), when does the thread actually exit?

Thanks.

Peter

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深海夜未眠 2024-11-15 22:07:35

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

通常,您会保留一个附加到io_service运行的线程或线程池,然后您将使用io_service安排您需要的任何事件。在 io_service 机制就位后,让 io_service 根据请求调度您的计划工作,然后您只需处理您安排的事件或工作请求使用io_service

As Sam hinted, depending on what you're attempting to accomplish, most of the time it is considered a design error to stop an io_service. You do not need to stop()/reset() the io_service in order to reschedule a timer.

Normally you would leave a thread or thread pool running attatched to an io_service and then you would schedule whatever event you need with the io_service. With the io_service machinery in place, leave it up to the io_service to dispatch your scheduled work as requested and then you only have to work with the events or work requests that you schedule with the io_service.

双马尾 2024-11-15 22:07:35

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

  1. io_service::reset() 仅应在之前的 io_service::run() 调用停止或运行完毕后调用,因为 文档描述
  2. 您不需要显式调用 Sleep(),只要有工作要做,对 io_service::run() 的调用就会阻塞。

It's not entirely clear to me what you are trying to accomplish, but there's a couple of things that are incorrect in the code you have posted.

  1. io_service::reset() should only be invoked after a previous invocation of io_service::run() was stopped or ran out of work as the documentation describes.
  2. you should not need explicit calls to Sleep(), the call to io_service::run() will block as long as it has work to do.
桃扇骨 2024-11-15 22:07:35

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

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

I figured it out, but I don't know why that I have to put io.reset() in Start(), since it's already been called in Stop().

See the updated code in the post.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文