【问题标题】:What is the equivalent of Qtimer in C++ using std or boost libraries?使用 std 或 boost 库的 C++ 中 Qtimer 的等价物是什么?
【发布时间】:2018-07-27 01:11:34
【问题描述】:

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

在 QT 中我可以这样做

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

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

谢谢

【问题讨论】:

  • 在不使用任何额外线程的情况下,请考虑您的程序始终在线程中运行。您可能想尝试 std::this_thread::sleep_for(std::chrono::milliseconds(5000));

标签: c++ boost timer std qtimer


【解决方案1】:

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

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

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

以下完整的程序,用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;
}

当您创建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 毫秒的间隔)。

【讨论】:

    【解决方案2】:

    您意识到 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
    

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

    【讨论】:

      猜你喜欢
      • 2012-04-07
      • 1970-01-01
      • 2014-11-22
      • 1970-01-01
      • 1970-01-01
      • 2016-10-18
      • 1970-01-01
      • 2012-09-11
      • 1970-01-01
      相关资源
      最近更新 更多