Using timers (interval, elapsed event) (🔴 Advanced)
How to create a simple timer operation with std, Qt and xtd.
This example show how to create a timer exectue it every 100 ms during 500 ms.
With standard modern C++23
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <print>
#include <thread>
using namespace std;
using namespace std::chrono;
class standard_timer {
public:
standard_timer(function<void()> callback, int interval_ms) : callback_(callback), interval_ms_(interval_ms) {}
~standard_timer() {close();}
void start() {
if (running_.load()) return;
running_.store(true);
thread_ = thread(&standard_timer::run, this);
}
void close() {
if (!running_.load()) return;
running_.store(false);
cv_.notify_one();
if (thread_.joinable()) thread_.join();
}
private:
void run() {
while (running_.load()) {
auto start_time = steady_clock::now();
callback_();
auto end_time = steady_clock::now();
auto elapsed = duration_cast<milliseconds>(end_time - start_time);
auto delay = milliseconds(interval_ms_) - elapsed;
if (delay > milliseconds(0)) {
unique_lock<mutex> lock(mutex_);
cv_.wait_for(lock, delay, [this] {return !running_.load();});
}
}
}
function<void()> callback_;
int interval_ms_ = 0;
atomic<bool> running_ = false;
thread thread_;
mutex mutex_;
condition_variable cv_;
};
auto main() -> int {
auto cpt = atomic<int> {0};
standard_timer t1([&] {
println("{:%H:%M:%S}.{:03} Timer ({}) ticked {} times ", system_clock::now(), (duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000).count(), this_thread::get_id(), cpt.fetch_add(1) + 1);
this_thread::sleep_for(milliseconds(200)); // Simulates work longer than the interval
}, 100);
t1.start();
this_thread::sleep_for(milliseconds(500));
println("{:%H:%M:%S}.{:03} Stop timer...", system_clock::now(), (duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000).count());
t1.close(); // Do not interrupt the current tick
println("{:%H:%M:%S}.{:03} timer stopped", system_clock::now(), (duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000).count());
}
// This example produces output similar to the following:
//
// 14:19:14.642 Timer (0x16fe87000) ticked 1 times
// 14:19:14.847 Timer (0x16fe87000) ticked 2 times
// 14:19:15.051 Timer (0x16fe87000) ticked 3 times
// 14:19:15.147 Stop timer...
// 14:19:15.255 timer stopped
- No real timer.
- You do everything by hand (thread + sleep).
- No precision, fast drift.
- A lot of code for a simple timer.
With Qt
#include <atomic>
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include <QThread>
class Worker : public QObject {
Q_OBJECT
public:
Worker(QObject *parent = nullptr) : QObject(parent) {}
public slots:
void startTimer() {
timer_ = std::make_unique<QTimer>(this);
timer_->setInterval(100);
connect(timer_.get(), &QTimer::timeout, this, &Worker::doWork);
timer_->start();
}
void stopTimer() {
timer_->stop();
emit finished();
}
private slots:
void doWork() {
qDebug().noquote() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "Timer (" << (quintptr)QThread::currentThreadId() << ") ticked" << cpt_.fetch_add(1) + 1 << "times";
QThread::msleep(200); // Simulates work longer than the interval
}
signals:
void finished();
private:
std::unique_ptr<QTimer> timer_;
std::atomic<int> cpt_ = 0;
};
auto main(int argc, char *argv[]) -> int {
auto app = QCoreApplication(argc, argv);
auto workerThread = QThread {};
auto worker = new Worker();
worker->moveToThread(&workerThread);
QObject::connect(&workerThread, &QThread::started, worker, &Worker::startTimer);
QObject::connect(worker, &Worker::finished, &workerThread, &QThread::quit);
QObject::connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
QObject::connect(&workerThread, &QThread::finished, &app, &QCoreApplication::quit);
workerThread.start();
QTimer::singleShot(500, [worker]() {
qDebug().noquote() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "Stop timer command sent...";
QMetaObject::invokeMethod(worker, "stopTimer", Qt::QueuedConnection);
qDebug().noquote() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "Timer stopped (Main Thread)";
});
return app.exec();
}
// This example produces output similar to the following:
//
// 14:00:11.291 Timer ( 6174191616 ) ticked 1 times
// 14:00:11.493 Timer ( 6174191616 ) ticked 2 times
// 14:00:11.695 Timer ( 6174191616 ) ticked 3 times
// 14:00:11.696 Stop timer command sent...
// 14:00:11.696 Timer stopped (Main Thread)
- QTimer is not a threading timer but a UI timer.
- No precision, fast drift.
- A lot of code for a simple timer.
With xtd
#include <xtd/xtd>
auto main() -> int {
auto cpt = 0;
console::write_line("{:HH:mm:ss.fff} ({}) Start timer...", date_time::now(), thread::main_thread().managed_thread_id());
auto t1 = threading::timer([&] {
console::write_line("{:HH:mm:ss.fff} Timer ({}) ticked {} times", date_time::now(), thread::current_thread().managed_thread_id(), interlocked::increment(cpt));
thread::sleep(200); // Simulates work longer than the interval
}, 0, 100);
thread::sleep(500);
console::write_line("{:HH:mm:ss.fff} Stop timer...", date_time::now());
t1.close(); // Do not interrupt the current tick
console::write_line("{:HH:mm:ss.fff} timer stopped", date_time::now());
thread_pool::close();
}
// This example produces output similar to the following:
//
// 11:43:56.180 Timer (11) ticked 1 times
// 11:43:56.325 Timer (8) ticked 2 times
// 11:43:56.451 Timer (6) ticked 3 times
// 11:43:56.580 Timer (9) ticked 4 times
// 11:43:56.684 Stop timer...
// 11:43:57.097 timer stopped
- No threads to create.
- No mutex.
- No event loop.
- The callback runs in a clean thread pool (no drift).
- Graceful closure (never cuts a tick in progress).
- API close to . NET but in modern C++.
- 0 boilerplate.
- 0 overhead.
- 0 depenency.
- 0 Heavy framework.
Quick Comparison
| Feature / Timer | std::thread DIY | Qt QTimer (Worker) | xtd::threading::timer |
|---|---|---|---|
| Threads created | ❌ 1 per timer | ❌ 1 (worker thread) | ✅ O (thread pool) |
| Mutex / Condition Variable | ❌ required | ❌ required (event loop) | ✅ Abstracted |
| Event loop / UI dependency | ❌ none | ❌ required | ✅ none |
| Callback precision | ⚠ low (fast drift) | ⚠ low (fast drift) | ✅ high (thread pool, no drift) |
| Tick interrupted on close | ⚠ sometimes | ⚠ sometimes | ✅ never |
| Boilerplate / overhead | ⚠ high | ⚠ high | ✅ minimal |
| Dependency | 🟢 standard lib | 🟡 Qt framework | 🟢 xtd only |
| API Code Lines | ~60 (Custom Class + Main) | ~50 (Worker Class + Main) | ~10 (Main only) |
| Easy to use API | ❌ | ❌ | ✅ |
Remarks
- Unlike standard C++ and Qt, xtd includes several timer classes, each of which offers different functionality:
- xtd::timers::timer, which fires an event and executes the code in one or more event sinks at regular intervals. The class is intended for use as a server-based or service component in a multithreaded environment; it has no user interface and is not visible at runtime.
- xtd::threading::timer, which executes a single callback method on a thread pool thread at regular intervals. The callback method is defined when the timer is instantiated and cannot be changed. Like the xtd::timers::timer class, this class is intended for use as a server-based or service component in a multithreaded environment; it has no user interface and is not visible at runtime.
- xtd::forms::timer, a Windows Forms component that fires an event and executes the code in one or more event sinks at regular intervals. The component has no user interface and is designed for use in a single-threaded environment; it executes on the UI thread.