我必须假设,“我不想使用线程”是指您不想在每次需要计时器时都在自己的代码中创建线程。那是因为没有线程实际上很难做到。
假设 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 毫秒的间隔)。