Skip to content

Commit cbe6a5b

Browse files
committed
Add Tips & tricks 11 : Using timers (interval, elapsed event)
1 parent 81d0e9a commit cbe6a5b

File tree

3 files changed

+237
-1
lines changed

3 files changed

+237
-1
lines changed

docs/documentation/internal/tips_and_triks_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
| 🔴 | #14 | January 5, 2026 | Displaying an open/save file dialog in one line | Qt QFileDialog / Win32 API |
3737
| 🔴 | #13 | December 22, 2025 | Drawing text and shapes in a form with xtd::drawing::graphics | Qt painting / GDI+ |
3838
| 🔴 | #12 | December 8, 2025 | Formatting strings with xtd::string::format | C++20 std::format / Qt QString::arg |
39-
| 🔴 | #11 | November 24, 2025 | Using timers (interval, elapsed event) | std::thread + sleep, Qt Timer |
39+
| 🟢 | #11 | November 24, 2025 | Using timers (interval, elapsed event) | std::thread + sleep, Qt Timer |
4040
| 🟢 | #10 | November 10, 2025 | Unit testing with xtd::tunit | Catch2 / gtest |
4141
| 🟢 | #9 | October 27, 2025 | Adding color to console output (text + background) | ANSI escape codes |
4242
| 🟢 | #8 | October 13, 2025 | array_list a heterogeneous container supporting multiple types | C++ modern / Boost / Qt |
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# Using timers (interval, elapsed event) (🔴 Advanced)
2+
3+
How to create a simple timer operation with std, Qt and xtd.
4+
5+
This example show how to create a timer exectue it every 100 ms during 500 ms.
6+
7+
8+
## With standard modern C++23
9+
10+
```cpp
11+
#include <atomic>
12+
#include <chrono>
13+
#include <condition_variable>
14+
#include <functional>
15+
#include <iostream>
16+
#include <mutex>
17+
#include <print>
18+
#include <thread>
19+
20+
using namespace std;
21+
using namespace std::chrono;
22+
23+
class standard_timer {
24+
public:
25+
standard_timer(function<void()> callback, int interval_ms) : callback_(callback), interval_ms_(interval_ms) {}
26+
~standard_timer() {close();}
27+
28+
void start() {
29+
if (running_.load()) return;
30+
running_.store(true);
31+
thread_ = thread(&standard_timer::run, this);
32+
}
33+
34+
void close() {
35+
if (!running_.load()) return;
36+
running_.store(false);
37+
cv_.notify_one();
38+
if (thread_.joinable()) thread_.join();
39+
}
40+
41+
private:
42+
void run() {
43+
while (running_.load()) {
44+
auto start_time = steady_clock::now();
45+
46+
callback_();
47+
48+
auto end_time = steady_clock::now();
49+
auto elapsed = duration_cast<milliseconds>(end_time - start_time);
50+
auto delay = milliseconds(interval_ms_) - elapsed;
51+
52+
if (delay > milliseconds(0)) {
53+
unique_lock<mutex> lock(mutex_);
54+
cv_.wait_for(lock, delay, [this] {return !running_.load();});
55+
}
56+
}
57+
}
58+
59+
function<void()> callback_;
60+
int interval_ms_ = 0;
61+
atomic<bool> running_ = false;
62+
thread thread_;
63+
mutex mutex_;
64+
condition_variable cv_;
65+
};
66+
67+
auto main() -> int {
68+
auto cpt = atomic<int> {0};
69+
standard_timer t1([&] {
70+
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);
71+
this_thread::sleep_for(milliseconds(200)); // Simulates work longer than the interval
72+
}, 100);
73+
t1.start();
74+
this_thread::sleep_for(milliseconds(500));
75+
println("{:%H:%M:%S}.{:03} Stop timer...", system_clock::now(), (duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000).count());
76+
t1.close(); // Do not interrupt the current tick
77+
println("{:%H:%M:%S}.{:03} timer stopped", system_clock::now(), (duration_cast<milliseconds>(system_clock::now().time_since_epoch()) % 1000).count());
78+
}
79+
80+
// This example produces output similar to the following:
81+
//
82+
// 14:19:14.642 Timer (0x16fe87000) ticked 1 times
83+
// 14:19:14.847 Timer (0x16fe87000) ticked 2 times
84+
// 14:19:15.051 Timer (0x16fe87000) ticked 3 times
85+
// 14:19:15.147 Stop timer...
86+
// 14:19:15.255 timer stopped
87+
```
88+
89+
* No real timer.
90+
* You do everything by hand (thread + sleep).
91+
* No precision, fast drift.
92+
* A lot of code for a simple timer.
93+
94+
## With Qt
95+
96+
```cpp
97+
#include <atomic>
98+
#include <QCoreApplication>
99+
#include <QDateTime>
100+
#include <QDebug>
101+
#include <QTimer>
102+
#include <QThread>
103+
104+
class Worker : public QObject {
105+
Q_OBJECT
106+
public:
107+
Worker(QObject *parent = nullptr) : QObject(parent) {}
108+
109+
public slots:
110+
void startTimer() {
111+
timer_ = std::make_unique<QTimer>(this);
112+
timer_->setInterval(100);
113+
connect(timer_.get(), &QTimer::timeout, this, &Worker::doWork);
114+
timer_->start();
115+
}
116+
117+
void stopTimer() {
118+
timer_->stop();
119+
emit finished();
120+
}
121+
122+
private slots:
123+
void doWork() {
124+
qDebug().noquote() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "Timer (" << (quintptr)QThread::currentThreadId() << ") ticked" << cpt_.fetch_add(1) + 1 << "times";
125+
QThread::msleep(200); // Simulates work longer than the interval
126+
}
127+
128+
signals:
129+
void finished();
130+
131+
private:
132+
std::unique_ptr<QTimer> timer_;
133+
std::atomic<int> cpt_ = 0;
134+
};
135+
136+
auto main(int argc, char *argv[]) -> int {
137+
auto app = QCoreApplication(argc, argv);
138+
139+
auto workerThread = QThread {};
140+
auto worker = new Worker();
141+
worker->moveToThread(&workerThread);
142+
143+
QObject::connect(&workerThread, &QThread::started, worker, &Worker::startTimer);
144+
QObject::connect(worker, &Worker::finished, &workerThread, &QThread::quit);
145+
QObject::connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
146+
QObject::connect(&workerThread, &QThread::finished, &app, &QCoreApplication::quit);
147+
148+
workerThread.start();
149+
QTimer::singleShot(500, [worker]() {
150+
qDebug().noquote() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "Stop timer command sent...";
151+
QMetaObject::invokeMethod(worker, "stopTimer", Qt::QueuedConnection);
152+
qDebug().noquote() << QDateTime::currentDateTime().toString("HH:mm:ss.zzz") << "Timer stopped (Main Thread)";
153+
});
154+
155+
return app.exec();
156+
}
157+
158+
// This example produces output similar to the following:
159+
//
160+
// 14:00:11.291 Timer ( 6174191616 ) ticked 1 times
161+
// 14:00:11.493 Timer ( 6174191616 ) ticked 2 times
162+
// 14:00:11.695 Timer ( 6174191616 ) ticked 3 times
163+
// 14:00:11.696 Stop timer command sent...
164+
// 14:00:11.696 Timer stopped (Main Thread)
165+
```
166+
167+
* QTimer is not a threading timer but a UI timer.
168+
* No precision, fast drift.
169+
* A lot of code for a simple timer.
170+
171+
## With xtd
172+
173+
```cpp
174+
#include <xtd/xtd>
175+
176+
auto main() -> int {
177+
auto cpt = 0;
178+
console::write_line("{:HH:mm:ss.fff} ({}) Start timer...", date_time::now(), thread::main_thread().managed_thread_id());
179+
auto t1 = threading::timer([&] {
180+
console::write_line("{:HH:mm:ss.fff} Timer ({}) ticked {} times", date_time::now(), thread::current_thread().managed_thread_id(), interlocked::increment(cpt));
181+
thread::sleep(200); // Simulates work longer than the interval
182+
}, 0, 100);
183+
thread::sleep(500);
184+
console::write_line("{:HH:mm:ss.fff} Stop timer...", date_time::now());
185+
t1.close(); // Do not interrupt the current tick
186+
console::write_line("{:HH:mm:ss.fff} timer stopped", date_time::now());
187+
thread_pool::close();
188+
}
189+
190+
// This example produces output similar to the following:
191+
//
192+
// 11:43:56.180 Timer (11) ticked 1 times
193+
// 11:43:56.325 Timer (8) ticked 2 times
194+
// 11:43:56.451 Timer (6) ticked 3 times
195+
// 11:43:56.580 Timer (9) ticked 4 times
196+
// 11:43:56.684 Stop timer...
197+
// 11:43:57.097 timer stopped
198+
```
199+
200+
* No threads to create.
201+
* No mutex.
202+
* No event loop.
203+
* The callback runs in a clean thread pool (no drift).
204+
* Graceful closure (never cuts a tick in progress).
205+
* API close to . NET but in modern C++.
206+
* 0 boilerplate.
207+
* 0 overhead.
208+
* 0 depenency.
209+
* 0 Heavy framework.
210+
211+
## Quick Comparison
212+
213+
| Feature / Timer | std::thread DIY | Qt QTimer (Worker) | xtd::threading::timer |
214+
|-----------------------------|---------------------------|---------------------------|---------------------------------|
215+
| Threads created | ❌ 1 per timer | ❌ 1 (worker thread) | ✅ O (thread pool) |
216+
| Mutex / Condition Variable | ❌ required | ❌ required (event loop) | ✅ Abstracted |
217+
| Event loop / UI dependency | ❌ none | ❌ required | ✅ none |
218+
| Callback precision | ⚠ low (fast drift) | ⚠ low (fast drift) | ✅ high (thread pool, no drift) |
219+
| Tick interrupted on close | ⚠ sometimes | ⚠ sometimes | ✅ never |
220+
| Boilerplate / overhead | ⚠ high | ⚠ high | ✅ minimal |
221+
| Dependency | 🟢 standard lib | 🟡 Qt framework | 🟢 xtd only |
222+
| API Code Lines | ~60 (Custom Class + Main) | ~50 (Worker Class + Main) | ~10 (Main only) |
223+
| Easy to use API ||||
224+
225+
## Remarks
226+
* Unlike standard C++ and Qt, xtd includes several timer classes, each of which offers different functionality:
227+
* [xtd::timers::timer](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1timers_1_1timer.html), 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.
228+
* [xtd::threading::timer](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1threading_1_1timer.html), 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.
229+
* [xtd::forms::timer](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1forms_1_1timer.html), 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.
230+
231+
## See also
232+
233+
* [xtd::threading](https://gammasoft71.github.io/xtd/reference_guides/latest/group__threading.html)
234+
* [Tips & Tricks](/docs/documentation/tips_and_tricks)
235+
* [Documentation](/docs/documentation)

docs/documentation/tips_and_tricks/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ You will find concrete solutions to common problems encountered with xtd in mode
1313

1414
| Category | Subject | Date | Level |
1515
| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ----- |
16+
| [![threading](/pictures/xtd.threading.png)](https://gammasoft71.github.io/xtd/reference_guides/latest/group__threading.html) | [#11 - Using timers (interval, elapsed event)](/docs/documentation/tips_and_tricks/11) | November 24, 2025 | 🔴 |
1617
| [![tunit](/pictures/xtd.tunit.png)](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__tunit.html) | [#10 - Unit testing with xtd.tunit](/docs/documentation/tips_and_tricks/10) | November 10, 2025 | 🟡 |
1718
| [![core](/pictures/xtd.core.png)](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__core.html) | [#9 - Adding color to console output (text + background)](/docs/documentation/tips_and_tricks/9) | October 27, 2025 | 🟢 |
1819
| [![core](/pictures/xtd.core.png)](https://gammasoft71.github.io/xtd/reference_guides/latest/group__xtd__core.html) | [#8 - xtd::collections::array_list a heterogeneous container supporting multiple types](/docs/documentation/tips_and_tricks/8) | October 13, 2025 | 🟢 |

0 commit comments

Comments
 (0)