使用std或boost库的C ++中的Qtimer等价于什么?

时间:2018-07-27 01:11:34

标签: c++ boost timer std qtimer

我必须每5秒执行一些任务,直到程序退出。我不想在这里使用线程。

在QT中,我可以这样

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);

但是我该如何在c ++中使用std或boost库呢?

谢谢

2 个答案:

答案 0 :(得分:4)

我必须假设,通过“我不想使用线程”,您的意思是不想在每次需要计时器时都在自己的代码中创建线程。那是因为在没有线程的情况下实际上很难。

假设使用C ++ 11,您实际上可以只使用核心语言 (不需要Boost或其他任何东西),并使用一个单独的类来处理线程,这样您就可以自己的代码就像(例如,用垃圾邮件骚扰您的前合伙人,一个相当可疑的用例):

    Periodic spamEx(std::chrono::seconds(60), SendEmaiToEx);

以下用g++ -std=c++11 -o periodic periodic.cpp -lpthread编译的完整程序将每秒运行一次定期回调函数,持续5秒(a)

#include <thread>
#include <chrono>
#include <functional>
#include <atomic>

// Not needed if you take couts out of Periodic class.
#include <iostream>

class Periodic {
public:
    explicit Periodic(
        const std::chrono::milliseconds &period,
        const std::function<void ()> &func
    )
        : m_period(period)
        , m_func(func)
        , m_inFlight(true)
    {
        std::cout << "Constructing periodic" << std::endl;
        m_thread = std::thread([this] {
            while (m_inFlight) {
                std::this_thread::sleep_for(m_period);
                if (m _inFlight) {
                    m_func();
                }
            }
        });
    }

    ~Periodic() {
        std::cout << "Destructed periodic" << std::endl;
        m_inFlight = false;
        m_thread.join();
        std::cout << "Destructed periodic" << std::endl;
    }

private:
    std::chrono::milliseconds m_period;
    std::function<void ()> m_func;
    std::atomic<bool> m_inFlight;
    std::thread m_thread;
};

// This is a test driver, the "meat" is above this.

#include <iostream>

void callback() {
    static int counter = 0;
    std::cout << "Callback " << ++counter << std::endl;
}

int main() {
    std::cout << "Starting main" << std::endl;
    Periodic p(std::chrono::seconds(1), callback);
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "Ending main" << std::endl;
}

创建Periodic的实例时,它会保存相关信息并启动线程来完成工作。线程(lambda)只是一个循环,该循环首先延迟一段时间,然后调用您的函数。它会继续执行此操作,直到析构函数指示它应该停止为止。

输出符合预期:

Starting main
Constructing periodic
Callback 1
Callback 2
Callback 3
Callback 4
Ending main
Destructed periodic

(a)请注意,上面给出的时间实际上是从一个回调的结束到下一个回调的开始的时间,不是从开始到开始的时间(我称之为真正的周期时间)。如果您的回调与期间相比足够快,则希望差异不会明显。

此外,线程无论如何都会执行此延迟,因此析构函数可能会延迟一整段时间才能返回。

如果要做需要开始到开始的时间段并快速清理,则可以使用以下线程代替。通过计算出回调的持续时间,并仅延迟该时间段的 rest (或如果该回调使用了整个时间段,则完全不延迟)来实现真正的开始到开始计时。 >

它也使用较小的睡眠,因此清理速度很快。线程函数为:

m_thread = std::thread([this] {
    // Ensure we wait the initial period, then start loop.

    auto lastCallback = std::chrono::steady_clock::now();
    while (m_inFlight) {
        // Small delay, then get current time.

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        auto timeNow = std::chrono::steady_clock::now();

        // Only callback if still active and current period has expired.

        if (m_inFlight && timeNow - lastCallback >= m_period) {
            // Start new period and call callback.

            lastCallback = timeNow;
            m_func();
        }
    }
});

请注意,如果您的回调花费的时间超过该时间段,则基本上将几乎连续地调用它(至少有100毫秒的间隔)。

答案 1 :(得分:1)

您意识到QTimer确实使用线程-或在主事件循环中轮询计时器。你也可以做到的。您可能遇到的概念性问题是您没有UI,因此可能未创建事件循环。

这是利用Boost Asio进行事件循环的最简单方法:

Live On Coliru

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

using namespace std::chrono_literals;
using boost::system::error_code;
namespace ba = boost::asio;

int main() {
    ba::io_service svc; // prefer io_context in recent boost versions
    ba::high_resolution_timer timer{svc};

    std::function<void()> resume;
    resume = [&] {
        timer.expires_from_now(50ms); // just for demo, don't wait 5s but 50ms

        timer.async_wait([=,&timer](error_code ec) {
            std::cout << "Timer: " << ec.message() << "\n";
            if (!ec) 
                resume();
        });
    };

    resume();
    svc.run_for(200ms); // probably getting 3 or 4 successful callbacks

    timer.cancel();
    svc.run(); // graceful shutdown
}

打印:

Timer: Success
Timer: Success
Timer: Success
Timer: Success
Timer: Operation canceled

根据应用程序的其余部分,这可能没有太大意义。在这种情况下,您可以执行相同操作,但使用单独的线程(是)来运行该事件循环。