How to eliminate msleep() from a worker thread and use a Timer in the GUI thread instead?
-
Hi,
I instantiate aSystemMonitorevery time thePerformanceMonitorpage gets pushed on myStackView, withSystemMonitorpushing several member functions to their own thread, each gathering some system statistics.The whole things works reasonably well, except that for the moment, the updates are initiated from within the threads of
SystemMonitorin updaters like the following:... void SystemMonitor::procUpdater() { while (m_running) { { QMutexLocker locker(procInfoMtx); procInfo->update(); //Somehow passing my model as signal arg does not work, //so retrieve it by callback... :-( //See https://forum.qt.io/topic/122095/qsortfilterproxymodel-as-signal-argument-is-undefined-in-qml emit procModelReady(); } QThread::msleep(UM_UPDATE_INTERVAL); } }One problem is that when the
PerformanceMonitorgets popped from the
StackViewand therefore theSystemMonitorgets destroyed, the UI blocks
for up to 1 second (UM_UPDATE_INTERVALis set to1000), depending in which part of themsleep()the threads are.SystemMonitor::~SystemMonitor() { ... { QMutexLocker locker(procInfoMtx); procUpdaterThread->quit(); procUpdaterThread->wait(); } }One solution would be to initiate updates from the UI via a
TimerinPerformanceMonitorinstead of making the threads sleep usingmsleep(), but that would imply creating and starting a thread each time the timer triggers, something like:void SystemMonitor::onRestartProcThreadRequested() { try { QMutexLocker lock(procInfoMtx) procUpdaterThread = QThread::create(std::bind(&SystemMonitor::procUpdater, this)); procUpdaterThread->start(); } catch (...) { ... } }and changing the threading function to something like:
void SystemMonitor::procUpdater() { QMutexLocker locker(procInfoMtx); procInfo->update(); emit procModelReady(); }There must be a there a better way to do this. Any suggestions will be much appreciated!
PS:
The rest of the implementation looks as follows:PerformanceMonitor.qml:Item { id: root ... SystemMonitor { id: sysmon } TableView { id: procTable } Connections { target: sysmon function onProcModelReady() { //Somehow passing my model as signal arg does not work, //so retrieve it by callback... :-( //See https://forum.qt.io/topic/122095/qsortfilterproxymodel-as-signal-argument-is-undefined-in-qml var model = sysmon.getProcProxy() if (model===null || model === procTable.model) return procTable.model = model } } }The system monitor is defined something as follows in C++:
class SystemMonitor : public QObject { Q_OBJECT SystemMonitor(QObject* parent=nullptr); ~SystemMonitor(); ... signals: void procModelReady(); ... private: volatile bool m_running; ... QThread* procUpdaterThread; ProcInfo::pointer procInfo; QMutex* procInfoMtx; void procUpdaterDaemon(); }SystemMonitor::SystemMonitor(QObject *parent) : QObject(parent) , m_running(true) ... , procUpdaterDaemonThread(nullptr) , procInfo(nullptr) , procInfoMtx(new QMutex()) { try { QMutexLocker locker(procInfoMtx); procInfo = ProcInfo::create(); procUpdaterThread = QThread::create(std::bind(&SystemMonitor::procUpdater, this)); procUpdaterThread->start(); } catch(ProcessesParseError& err) {...} }In
main.cpp:qmlRegisterType<SystemMonitor>("com.dirac", 1, 0, "SystemMonitor"); qmlRegisterUncreatableType<ProcessProxyModel>("com.dirac", 1, 0, "ProcessProxyModel", "Type is not allowed to be instantiated"); -
Have you considered using a
QTimerin your thread, instead of sleeping? That would open up the possibility of being able to request the thread to terminate without it blocking before the next update.Your thread would need its own event loop to allow
QTimerto be used. -
Have you considered using a
QTimerin your thread, instead of sleeping? That would open up the possibility of being able to request the thread to terminate without it blocking before the next update.Your thread would need its own event loop to allow
QTimerto be used.Thanks @Bob64!
@Bob64 said in How to eliminate msleep() from a worker thread and use a Timer in the GUI thread instead?:
Have you considered using a QTimer in your thread, instead of sleeping?
That is also what the Qt
QThreaddoc suggests, but I didn't really understand how to achieve that.@Bob64 said in How to eliminate msleep() from a worker thread and use a Timer in the GUI thread instead?:
Your thread would need its own event loop to allow QTimer to be used.
How would I go about that? Any pointers on how to implement such an event loop inside a thread?
Edit: The Qt docs says that
start(...)callsrun()and the default implementation ofrun()simply callsexec()whichenters the event loop and waits until exit() is calledSo, by default there seems to be an event loop associated with a
QThread. How can I the use aQTimerwith it to replacemsleep()? -
Hi,
If you really need threading then use the worker object approach. No need to subclass QThread at all.
Depending on the number of monitors you have you might even be able to just have them all using a single thread.
-
Hi,
If you really need threading then use the worker object approach. No need to subclass QThread at all.
Depending on the number of monitors you have you might even be able to just have them all using a single thread.
@SGaist
I think I am starting to see the light ...
So in theControllerpart of theWorkerexample of the Qt doc, all I need to do then is to create aQTimerand configure that one to emitoperate(...)signals at the desired rate (or directly connect the timertimeoutsignal todoWork)Thanks a lot!
-
I just completed the conversion to the
Workerobject method, and it works beautifully.@SGaist said in How to eliminate msleep() from a worker thread and use a Timer in the GUI thread instead?:
Depending on the number of monitors you have you might even be able to just have them all using a single thread.
That works indeed, and the code is now much cleaner and concise.
Thanks again @SGaist!