使用 std 或 boost 库的 C++ 中的 Qtimer 相当于什么?

See*_*ker 3 c++ boost timer std qtimer

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

在 QT 我可以这样做

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
Run Code Online (Sandbox Code Playgroud)

但是我如何使用 std 或 boost 库在 C++ 中做到这一点?

谢谢

pax*_*blo 5

我必须假设,“我不想使用线程”是指您不想在每次需要计时器时在自己的代码中创建线程。那是因为在没有线程的情况下做它实际上非常困难。

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

    Periodic spamEx(std::chrono::seconds(60), SendEmaiToEx);
Run Code Online (Sandbox Code Playgroud)

以下完整程序,编译g++ -std=c++11 -o periodic periodic.cpp -lpthread后将每秒运行一个周期性回调函数,持续五秒(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;
}
Run Code Online (Sandbox Code Playgroud)

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

正如预期的那样,输出是:

Starting main
Constructing periodic
Callback 1
Callback 2
Callback 3
Callback 4
Ending main
Destructed periodic
Run Code Online (Sandbox Code Playgroud)

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

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

如果您确实需要一个从头到尾的周期和快速清理,您可以改用以下线程。它通过计算回调的持续时间并仅延迟剩余时间(或者如果回调使用整个时间段则根本不延迟)来实现真正的开始计时。

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

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();
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

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