Я должен предположить, что под «я не хочу использовать поток» вы имеете в виду, что не хотите создавать потоки в своем собственном коде каждый раз, когда вам нужен таймер. Это потому, что делать это без потоков на самом деле довольно сложно.
Предполагая 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
, он сохраняет соответствующую информацию и запускает поток для выполнения работы. Поток (лямбда) - это просто цикл, который сначала задерживает на период, а затем вызывает вашу функцию. Он продолжает делать это до тех пор, пока деструктор не укажет, что он должен остановиться.
Вывод, как и ожидалось:
Starting main
Constructing periodic
Callback 1
Callback 2
Callback 3
Callback 4
Ending main
Destructed periodic
(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();
}
}
});
Имейте в виду, что если ваш обратный вызов занимает больше времени, чем период, вы в основном будете вызывать его почти непрерывно (по крайней мере, будет разрыв в 100 мс).
person
paxdiablo
schedule
27.07.2018