I am working on a data logger in QT framework. I Intend to save log strings to files and print to the console in a separate watcher thread. In that separate thread I need to watch my QStringList for new items added. If there are new items I deque them and log. I was wondering what is the mechanism used for this in Qt framework. In STD lib i used condition_variable for this task like this:
/*!
* \brief puts a \ref logline_t object at the end of the queue
* #param s object to be added to queue
*/
void CLogger::push_back(logline_t* s)
{
unique_lock<mutex> ul(m_mutex2);
s->queSize = m_data.size();
m_data.emplace_back(move(s));
m_cv.notify_all();
}
/*!
* \brief takes a \ref logline_t object from the beggining of the queue
* #return first \ref logline_t object
*/
CLogger::logline_t CLogger::pop_front()
{
unique_lock<mutex> ul(m_mutex2);
m_cv.wait(ul, [this]() { return !m_data.empty(); });
assert(m_data.front());
logline_t retVal = move(*m_data.front());
delete m_data.front();
m_data.front() = NULL;
m_data.pop_front();
return retVal;
}
m_cv is the conditional variable object. How can this functionality be acquired with QT? I bet there is a lot better way :). I would appreciate all help.
Ps: I know pointer functions parameters are not asserted, this is an old code... :P
There are a couple of ways of doing the notification in Qt.
Signals and Slots
Signals and slots can be sent between threads, when making the connection, you set the connection type to Qt::QueuedConnection or Qt::BlockingQueuedConnection.
You may want to create a wrapper class around the QStringList so that modifications can trigger the signals that other classes listen for.
QMutex and QWaitCondition
You can use the Qt thread synchronisation classes QMutex and QWaitCondition to do the classic manual synchronisation like you have done previously. When using QMutex, the QMutexLocker is useful for scope lock and release of a QMutex.
Here's an example of doing this signals and slots. You may want to do your own benchmarks to test whether this fits your needs. Please also note that while signals and slots guarantee thread-safety, they don't guarantee that the messages will appear in the same order they were sent. That being said, I've used this mechanism for years and it has yet to happen for me.
First, create a couple of classes:
Loggee
// loggee.hpp
#ifndef LOGGEE_HPP
#define LOGGEE_HPP
#include <QObject>
class Loggee : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
void someEventHappened(int id);
signals:
void newLogLineNotify(QString const&);
};
#endif // LOGGEE_HPP
and the .cpp file:
#include "loggee.hpp"
void Loggee::someEventHappened(int id)
{
auto toLog = QString::number(id);
emit newLogLineNotify(toLog);
}
Logger
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <QObject>
class Logger : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
signals:
public slots:
void onLogEventHappened(QString const&);
};
#endif // LOGGER_HPP
and the .cpp file:
#include <QDebug>
#include <QThread>
#include "logger.hpp"
void Logger::onLogEventHappened(QString const& str)
{
qDebug() << QThread::currentThreadId() << str;
}
the rest
#include <QDebug>
#include <QThread>
#include <QCoreApplication>
#include <QTimer>
#include "logger.hpp"
#include "loggee.hpp"
int main(int argc, char** argv)
{
QCoreApplication a(argc, argv);
QThread t;
t.start();
Logger foo;
Loggee bar;
foo.moveToThread(&t);
QObject::connect(&bar, &Loggee::newLogLineNotify,
&foo, &Logger::onLogEventHappened,
Qt::AutoConnection);
qDebug() << "Current thread id: " << QThread::currentThreadId();
bar.someEventHappened(42);
QTimer::singleShot(3000, [&]{ bar.someEventHappened(43); });
return a.exec();
}
In this demo, I create a QThread and a Logger, move handling of this new Logger's slots to this new thread's event loop. Then I connect Loggee's signal with Logger's slot using Qt::AutoConnection. This is the default but I stated it explicitly to show that you can change this (i.e. to Qt::QueuedConnection which would queue the execution of the slot even if both threads lived in the same thread).
Then I cause the Loggee to emit¹ a singal. It gets properly scheduled and causes the logging slot to be executed in the receiver's thread.
¹ emit is #define emit /*null*/, so you can omit it if you want
If you are just looking for the Qt equivalents to the classes you have been using:
std::mutex -> QMutex
std::condition_variable -> QWaitCondition
std::unique_lock -> QMutexLocker
Related
I believe I've found a bug in the Windows' Qt 5 implementation. It doesn't reproduce with Qt 6, so I don't think I should post it to Qt's maintainers now. But still I'd like to ask here (1) if it's a bug indeed (or is my code just incorrect somewhere), and (2) what workaround can I write to avoid this issue, provided that I can't upgrade to Qt 6 right now.
I have a class BackgroundExecutor which owns a QThread and has a function for posting new tasks (std::function instances) to it. In its destructor, BackgroundExecutor calls quit and wait member functions of its thread object.
Things get interesting when one of the posted tasks processed by the background QThread happens to have executed some external QProcess (I think it affects the state of the thread's QEventLoop somehow). In this case, the wait call on a QThread has a chance to hang forever.
The call stack of the main thread looks like this:
ntdll.dll!NtWaitForSingleObject() Unknown
KernelBase.dll!WaitForSingleObjectEx() Unknown
> Qt5Cored.dll!QThread::wait(QDeadlineTimer deadline) Line 630 C++
QThreadAndQProcessBug.exe!BackgroundExecutor::~BackgroundExecutor() Line 87 C++
QThreadAndQProcessBug.exe!RunTest() Line 123 C++
QThreadAndQProcessBug.exe!RunTestMultipleTimes() Line 132 C++
And here's the call stack of the background thread:
win32u.dll!NtUserMsgWaitForMultipleObjectsEx() Unknown
user32.dll!RealMsgWaitForMultipleObjectsEx() Unknown
> Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 625 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 232 C++
Qt5Cored.dll!QThread::exec() Line 547 C++
Qt5Cored.dll!QThread::run() Line 617 C++
Qt5Cored.dll!QThreadPrivate::start(void * arg) Line 407 C++
It's stuck at the line 625 (as of Qt 5.15.2) of "qeventdispatcher_win.cpp", inside the QEventDispatcherWin32::processEvents function: waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);.
The full text of the program which reproduces the issue (though it might take some time - one of my PCs requires only 1000 iterations on average, while the other one might execute 100'000 iterations before the hang would occur):
#include <QCoreApplication>
#include <QTimer>
#include <QObject>
#include <QProcess>
#include <QThread>
#include <functional>
#include <future>
#include <memory>
#include <iostream>
Q_DECLARE_METATYPE(std::function<void()>); // for passing std::function<void()> through Qt's signals
static void EnsureStdFunctionOfVoidMetaTypeRegistered()
{
static std::once_flag std_function_metatype_registered{};
std::call_once(std_function_metatype_registered, []() {
qRegisterMetaType<std::function<void()>>("std::function<void()>");
});
}
class WorkerObject; // worker object that "lives" in a background thread of a BackgroundExecutor
class BackgroundExecutor final
{
public:
BackgroundExecutor();
~BackgroundExecutor();
// posts a new task for the background QThread,
// returns a std::future which can be waited on to ensure the task is done
[[nodiscard]] std::future<void> PostTask(std::function<void()> task);
private:
WorkerObject* _background_worker = nullptr;
QThread* _qt_thread = nullptr;
};
class WorkerObject final : public QObject
{
Q_OBJECT;
public:
WorkerObject()
{
connect(this, &WorkerObject::TaskPosted, this, &WorkerObject::ProcessPostedTask);
}
// can be called from any thread;
// "moves" the task to the background worker thread via Qt's signals/slots mechanism
// so that it could be processed there
void PostTask(const std::function<void()>& task)
{
EnsureStdFunctionOfVoidMetaTypeRegistered();
Q_EMIT TaskPosted(task);
}
private Q_SLOTS:
void ProcessPostedTask(const std::function<void()>& posted_task)
{
std::invoke(posted_task);
}
Q_SIGNALS:
void TaskPosted(const std::function<void()>&);
};
BackgroundExecutor::BackgroundExecutor()
{
{
std::unique_ptr<QThread> qt_thread_safe(new QThread()); // exception safety
_background_worker = new WorkerObject();
_qt_thread = qt_thread_safe.release();
}
_background_worker->moveToThread(_qt_thread);
QObject::connect(_qt_thread, &QThread::finished, _background_worker, &WorkerObject::deleteLater);
QObject::connect(_qt_thread, &QThread::finished, _qt_thread, &QThread::deleteLater);
_qt_thread->start();
}
BackgroundExecutor::~BackgroundExecutor()
{
_qt_thread->quit();
_qt_thread->wait(); // !!! might hang !!!
}
[[nodiscard]] std::future<void> BackgroundExecutor::PostTask(std::function<void()> task)
{
std::shared_ptr task_promise = std::make_shared<std::promise<void>>();
std::future task_future = task_promise->get_future();
std::function<void()> task_wrapper = [task_promise = std::move(task_promise), task = std::move(task)]()
{
std::invoke(task);
task_promise->set_value();
};
_background_worker->PostTask(task_wrapper);
return task_future;
}
static void RunQProcessAndWaitForFinished()
{
QProcess process;
process.setProgram("C:\\Windows\\System32\\cmd.exe");
process.setArguments({ "/C", "C:\\Windows\\System32\\timeout.exe", QString::number(30) });
process.start();
process.waitForStarted(-1);
process.waitForFinished(-1);
}
static void RunTest()
{
BackgroundExecutor executor;
std::future task_future = executor.PostTask([]() {
RunQProcessAndWaitForFinished();
});
task_future.get();
}
static void RunTestMultipleTimes()
{
constexpr int repeat = 500'000;
for (int i = 0; i < repeat; ++i)
{
std::cout << "starting iteration " << i << '\n';
RunTest();
}
std::cout << "all iterations finished" << '\n';
}
int main(int argc, char** argv)
{
QCoreApplication qt_app{ argc, argv };
QTimer::singleShot(
0,
[&]()
{
RunTestMultipleTimes();
qt_app.exit(0);
});
return qt_app.exec();
}
#include "main.moc"
In Qt 5.12.12, all iterations are complete. Did you try this version?
I just split classes into separate files to implicitly include the moc file and I made a .pro file.
QT += core
CONFIG += c++17 console
CONFIG -= app_bundle
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
backgroundexecutor.cpp \
main.cpp \
workerobject.cpp
HEADERS += \
backgroundexecutor.h \
workerobject.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
After posting this question, I also ran some tests with Qt 5.12.12 and Qt 5.15.5, as suggested in the comments. I could reproduce the bug in neither of these versions, while in Qt 5.15.2 it continues to reproduce consistently. This could either mean that the bug is really absent in these versions, or that it is much more rarely reproducible there (at least on my PCs) and I didn't try enough.
In any case, I would like to share a workaround which I came up with after consulting one of my colleagues who has more experience with WinAPI than I do. Hope it will also help someone else in the future, if they happen to be stuck with the Qt version where this bug is present.
Here's the code (you can compare it with the original listing from the question using a tool such as WinMerge, or just look at all the parts near the WORKAROUND_FOR_QTHREAD_WAIT macro):
#include <QCoreApplication>
#include <QTimer>
#include <QObject>
#include <QProcess>
#include <QThread>
#include <atomic>
#include <thread>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <cassert>
#include <iostream>
#ifdef _WIN32
#define WORKAROUND_FOR_QTHREAD_WAIT 1
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif // _WIN32
Q_DECLARE_METATYPE(std::function<void()>); // for passing std::function<void()> through Qt's signals
static void EnsureStdFunctionOfVoidMetaTypeRegistered()
{
static std::once_flag std_function_metatype_registered{};
std::call_once(std_function_metatype_registered, []() {
qRegisterMetaType<std::function<void()>>("std::function<void()>");
});
}
class WorkerObject; // worker object that "lives" in a background thread of a BackgroundExecutor
class BackgroundExecutor final
{
public:
BackgroundExecutor();
~BackgroundExecutor();
// posts a new task for the background QThread,
// returns a std::future which can be waited on to ensure the task is done
[[nodiscard]] std::future<void> PostTask(std::function<void()> task);
private:
WorkerObject* _background_worker = nullptr;
QThread* _qt_thread = nullptr;
};
class WorkerObject final : public QObject
{
Q_OBJECT;
public:
WorkerObject()
{
connect(this, &WorkerObject::TaskPosted, this, &WorkerObject::ProcessPostedTask);
}
// can be called from any thread;
// "moves" the task to the background worker thread via Qt's signals/slots mechanism
// so that it could be processed there
void PostTask(const std::function<void()>& task)
{
EnsureStdFunctionOfVoidMetaTypeRegistered();
Q_EMIT TaskPosted(task);
}
#if WORKAROUND_FOR_QTHREAD_WAIT
[[nodiscard]] DWORD GetBackgroundThreadID() const
{
while (_windows_thread_id_initialized.load(std::memory_order_acquire) == false)
std::this_thread::yield();
return _windows_thread_id;
}
#endif // WORKAROUND_FOR_QTHREAD_WAIT
public Q_SLOTS:
void InitializeBackgroundThreadID()
{
#if WORKAROUND_FOR_QTHREAD_WAIT
_windows_thread_id = ::GetCurrentThreadId();
_windows_thread_id_initialized.store(true, std::memory_order_release);
#else
assert(false && "shouldn't have been invoked in this configuration");
#endif // WORKAROUND_FOR_QTHREAD_WAIT
}
void ProcessPostedTask(const std::function<void()>& posted_task)
{
std::invoke(posted_task);
}
Q_SIGNALS:
void TaskPosted(const std::function<void()>&);
#if WORKAROUND_FOR_QTHREAD_WAIT
private:
DWORD _windows_thread_id;
std::atomic<bool> _windows_thread_id_initialized{ false };
#endif // WORKAROUND_FOR_QTHREAD_WAIT
};
BackgroundExecutor::BackgroundExecutor()
{
{
std::unique_ptr<QThread> qt_thread_safe(new QThread()); // exception safety
_background_worker = new WorkerObject();
_qt_thread = qt_thread_safe.release();
}
_background_worker->moveToThread(_qt_thread);
#if WORKAROUND_FOR_QTHREAD_WAIT
QMetaObject::invokeMethod(_background_worker, "InitializeBackgroundThreadID", Qt::QueuedConnection);
#endif // WORKAROUND_FOR_QTHREAD_WAIT
QObject::connect(_qt_thread, &QThread::finished, _background_worker, &WorkerObject::deleteLater);
QObject::connect(_qt_thread, &QThread::finished, _qt_thread, &QThread::deleteLater);
_qt_thread->start();
}
BackgroundExecutor::~BackgroundExecutor()
{
_qt_thread->quit();
#if WORKAROUND_FOR_QTHREAD_WAIT
const DWORD background_thread_id = _background_worker->GetBackgroundThreadID();
while (_qt_thread->wait(1'000) == false)
// "awaken" the MsgWaitForMultipleObjectsEx function call in which the background thread got stuck
// by posting a fake "message" to it, so that it would snap out of it, check its exit flag and finish properly
::PostThreadMessage(background_thread_id, WM_NULL, 0, 0);
#else
_qt_thread->wait();
#endif // WORKAROUND_FOR_QTHREAD_WAIT
}
[[nodiscard]] std::future<void> BackgroundExecutor::PostTask(std::function<void()> task)
{
std::shared_ptr task_promise = std::make_shared<std::promise<void>>();
std::future task_future = task_promise->get_future();
std::function<void()> task_wrapper = [task_promise = std::move(task_promise), task = std::move(task)]()
{
std::invoke(task);
task_promise->set_value();
};
_background_worker->PostTask(task_wrapper);
return task_future;
}
static void RunQProcessAndWaitForFinished()
{
QProcess process;
process.setProgram("C:\\Windows\\System32\\cmd.exe");
process.setArguments({ "/C", "C:\\Windows\\System32\\timeout.exe", QString::number(30) });
process.start();
process.waitForStarted(-1);
process.waitForFinished(-1);
}
static void RunTest()
{
BackgroundExecutor executor;
std::future task_future = executor.PostTask([]() {
RunQProcessAndWaitForFinished();
});
task_future.get();
}
static void RunTestMultipleTimes()
{
constexpr int repeat = 500'000;
for (int i = 0; i < repeat; ++i)
{
std::cout << "starting iteration " << i << '\n';
RunTest();
}
std::cout << "all iterations finished" << '\n';
}
int main(int argc, char** argv)
{
QCoreApplication qt_app{ argc, argv };
QTimer::singleShot(
0,
[&]()
{
RunTestMultipleTimes();
qt_app.exit(0);
});
return qt_app.exec();
}
#include "main.moc"
And now for the implementation details.
The basic idea is: the MsgWaitForMultipleObjectsEx(..., QS_ALLINPUT, ...) function call (in which the background thread gets stuck) can be "awakened" by a message posted to this thread's queue, since QS_ALLINPUT also implies QS_POSTMESSAGE as a part of its "wake mask". This can be done by calling PostThreadMessage from the destroying (main) thread in case we've detected that the background one got stuck. And since we don't care about a particular type of a message (and actually don't want to send any "meaningful" message), WM_NULL will do the trick.
So, the destructor of our BackgroundExecutor class should look like this:
BackgroundExecutor::~BackgroundExecutor()
{
_qt_thread->quit();
const DWORD background_thread_id = _background_worker->GetBackgroundThreadID();
while (_qt_thread->wait(1'000) == false)
::PostThreadMessage(background_thread_id, WM_NULL, 0, 0);
}
Now the question is, how do we get this DWORD background_thread_id value. Qt doesn't provide us an easy way of getting it from a QThread object (there is a function currentThreadId, but it is static and returns a DWORD ID of the currently executing thread, thus it's not what we want here). Instead, we will call GetCurrentThreadId from the background thread at some early point of its execution, store it in our WorkerObject, and retrieve in the main thread later.
In order to do that, we can write a new slot InitializeBackgroundThreadID in the WorkerObject class and invoke it via Qt::QueuedConnection from the BackgroundExecutor's constructor after moving the worker object to the background thread (thus ensuring that the slot will be invoked from that thread):
public Q_SLOTS:
void InitializeBackgroundThreadID()
{
_windows_thread_id = ::GetCurrentThreadId();
_windows_thread_id_initialized.store(true, std::memory_order_release);
}
(here, _windows_thread_id is a DWORD member variable of the WorkerObject, while _windows_thread_id_initialized is a std::atomic<bool> guard flag required for cross-thread synchronization via the synchronizes-with relation)
In BackgroundExecutor's constructor:
...
_background_worker->moveToThread(_qt_thread);
QMetaObject::invokeMethod(_background_worker, "InitializeBackgroundThreadID", Qt::QueuedConnection);
...
Now we can implement the getter for this thread ID:
[[nodiscard]] DWORD GetBackgroundThreadID() const
{
while (_windows_thread_id_initialized.load(std::memory_order_acquire) == false)
std::this_thread::yield();
return _windows_thread_id;
}
In practice, by the time the BackgroundExecutor's destructor is called, the thread ID will have been already initialized, so the loop won't spin or call yield at all: it is just needed to guarantee the synchronizes-with relation (the main thread will be guaranteed to read the correct value of _windows_thread_id after it has read true from _windows_thread_id_initialized via a load-acquire operation).
There can be other means of implementing this part, e.g., we could make the DWORD _windows_thread_id member variable an atomic itself, providing it some "invalid" sentry value for the "uninitialized" state (Raymond Chen once wrote zero should be okay for that). But these are just the implementation details, the basic idea still remains the same.
I created a minimal QT GUI example to update widgets from a worker thread based on the recommended approach in the The QThread 5.12 documentation.
As described in the QThread 5.12 documentation, the Worker class (with a potentially long void doWork(const QString ¶meter) method is:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
and the corresponding Controller class is:
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
Unlike sub-classing from a QThread, the approach shown in the documentation shows the recommended way that uses a controller and a worker that extends QObject rather than extending QThread and overriding the QThread::run method, however it does not show how these should be used in the context of a real example.
I need to use an QT Worker thread that updates widgets on a GUI using a timer.
I also need to be able to halt and restart/relaunch this thread with different parameters and I am having some trouble with how to do this correctly. indicates the preferred way to do this via a Controller and a Worker but the connect logic is a bit confusing.
The place where I need help is how to properly integrate the timer in my worker thread and also how to stop and restart a replacement worker when the current one has either finished or been interrupted and restarted.
My working code is made up of the following files.
Controller.h
#pragma once
// SYSTEM INCLUDES
#include <QObject>
#include <QThread>
// APPLICATION INCLUDES
#include "Worker.h"
// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller(/*MainWindow* mainWindow*/) {
auto worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &) {
// how do I update the mainWindow from here
}
signals:
void operate(int);
};
Worker.h
#pragma once
// SYSTEM INCLUDES
#include <QTimer>
#include <QObject>
#include <QEventLoop>
// APPLICATION INCLUDES
#include "Worker.h"
// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(int count) {
QString result = "finished";
// Event loop allocated in workerThread
// (non-main) thread affinity (as moveToThread)
// this is important as otherwise it would occur
// on the main thread.
QEventLoop loop;
for (auto i=0; i< count; i++) {
// wait 1000 ms doing nothing...
QTimer::singleShot(1000, &loop, SLOT(quit()));
// process any signals emitted above
loop.exec();
emit progressUpdate(i);
}
emit resultReady(result);
}
signals:
void progressUpdate(int secondsLeft);
void resultReady(const QString &result);
};
MainWindow.h - I needed to add a Controller member here. I also added an updateValue slot here where I wish to update the GUI. Unfortunately I don't know how to get the controller or the worker to connect a signal from the thread to update this slot.
#pragma once
// SYSTEM INCLUDES
#include <memory>
#include <QMainWindow>
// APPLICATION INCLUDES
// DEFINES
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS
namespace Ui {
class MainWindow;
}
class Controller;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void updateValue(int secsLeft);
private:
Ui::MainWindow *ui;
std::unique_ptr<Controller> mpController;
};
MainWindow.cpp -
#include <QThread>
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "Controller.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, mpController(std::make_unique<Controller>())
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
emit mpController->operate(100);
}
void MainWindow::updateValue(int secsLeft)
{
ui->secondsLeft->setText(QString::number(secsLeft));
}
and finally main.cpp
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I basically need help and an explanation on how I should use the QT Thread's controller/worker integrated in my GUI.
I'll try to answer all the issues you're addressing in your question:
I don't know how to get the controller or the worker to connect a signal from the thread to update this slot.
You got that almost right yourself.
Your Worker lives within the event loop of your Controller:
+--GUI-thread--+ (main event loop)
| MainWindow, |
| Controller --o-----> +--QThread--+ (own event loop in ::exec())
+--------------+ | Worker |
+-----------+
Communication between Controller and Worker must happen through signal-slot-connections. In between MainWindow and Controller signals help keep dependencies to a minimum.
You can imagine Controller as a kind of relay: Commands from MainWindow get forwarded through Controller to the Worker. Results from Worker get forwarded through the Controller to anyone who is interested.
For this, you can simply define signals in Controller:
class Controller : public QObject
{
//...
signals:
void SignalForwardResult(int result);
};
and then instead of
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
use the new signal:
connect(worker, &Worker::resultReady, this, &Controller::SignalForwardResult);
// Yes, you can connect a signal to another signal the same way you would connect to a slot.
and in your MainWindow constructor:
//...
ui->setupUi(this);
connect(mpController, &Controller::SignalForwardResult, this, &MainWindow::displayResult);
Likewise for Worker::progressUpdate() -> Controller::SignalForwardProgress() -> MainWindow::updateValue().
how to stop and restart a replacement worker when the current one has either finished or been interrupted and restarted.
Either create a new worker for each task or use a persistent worker that can react on new task requests.
You start a task by sending it to the worker ::doWork() function.
A task ends by itself when the long work is finished. You get a notification via the worker's resultReady signal.
Cancelling a task is only possible by intervention
If you indeed have a QTimer, you can use a cancel() slot because that will be invoked in the thread's event loop before the next timeout.
If you have a long-running calculation, you need to share some token that you read from inside your calculation method and set from your GUI thread. I usually use a shared QAtomicInt pointer for that, but a shared bool usually suffices too.
Note that while a method is running on a thread, that thread's event loop is blocked and won't receive any signals until the method is finished.
DON'T use QCoreApplication::processEvents() except if you really know, what you're doing. (And expect that you don't!)
how to properly integrate the timer in my worker thread
You shouldn't.
I guess you use a background thread because there is so much work to do or you need to blocking wait for so long that it would block the GUI, right? (If not, consider not using threads, saves you a lot of headaches.)
If you need a timer, make it a member of Worker and set its parentObject to the Worker instance. This way, both will always have the same thread affinity. Then, connect it to a slot, like Worker::timeoutSlot(). There you can emit your finish signal.
I was wondering how I can run a function when closing the terminal (I'm on Linux) of my Qt Console Application. I have a 'static' class which holds all the variables I need (I know I can use singleton but I like this way better) and it has a function called destroy, this will delete all pointers).
So this is my .cpp of the 'static' class:
#include "engine.h"
logging* engine::m_logging;
tcp_listener* engine::m_tcp_listener;
void engine::initialize()
{
engine::m_logging = new logging();
engine::m_logging->write_line("Initializing Fullmoon Messenger server...");
engine::m_tcp_listener = new tcp_listener();
engine::m_tcp_listener->start("127.0.0.1", 30000);
}
void engine::destroy()
{
delete engine::m_logging;
}
logging* engine::get_logging()
{
return engine::m_logging;
}
And this is my main.cpp:
#include <QCoreApplication>
#include "engine.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
engine::initialize();
return a.exec();
}
How can I call engine::destroy when closing the application? I assume there's a signal for it.
You need to handle the POSIX signal SIGHUP for this.
(Note: POSIX signals are something completely different than Qt signals, so they're not handled with "slots". Don't confuse them.)
Note that your options for what to do in a POSIX signal handler are very limited. The man page signal(7) lists the functions which are safe to be called. In the comments to this answer, Kuba Ober pointed out that you should to stick to the official documentation for handling POSIX signals in Qt applications. But since the process as explained in the documentation is quite complicated, I wrapped it in a reusable class PosixSignalProxy, converting POSIX signals to Qt signals.
If you use this proxy class you just have to write:
PosixSignalProxy<SIGHUP> sighupProxy;
QObject::connect(&sighupProxy, &PosixSignalProxyBase::receivedSignal, []{
engine::destroy();
});
The class is found at the bottom of this post.
Note that only one instance of a PosixSignalProxy can be created for each POSIX signal type. If you need to do multiple things when receiving a signal, you can however connect to the Qt signal multiple times.
Also, you might be interested in similar signals for different external reasons for quitting your application, such as killing it or hitting Ctrl+C in the terminal window.
SIGHUP (as in the example above): when the terminal window was closed (and the parent process was destroyed)
SIGINT: when the user hits Ctrl+C - Note that when you handle this signal you need to quit at the end of the handler since your process is responsible of finally quitting itself. You overwrite this default behavior and have the option not to only cleanup stuff but also to ignore the user's wish to quit the application.
SIGTERM: when the process is killed with e.g. kill (not to be confused with SIGKILL which is a more aggressive way to kill a process; you can't handle that one)
Here is the class definition:
posixsignalproxy.h
#ifndef POSIXSIGNALPROXY_H
#define POSIXSIGNALPROXY_H
#include <QObject>
class QSocketNotifier;
typedef void (*PosixSignalHandlerType) (int);
// Helper base class (no template) - Do not use this class directly.
class PosixSignalProxyBase : public QObject
{
Q_OBJECT
public:
PosixSignalProxyBase(
QObject *parent,
int (&sockets)[2],
int posixSignalNumber,
PosixSignalHandlerType posixSignalHandler,
int posixSignalFlags);
~PosixSignalProxyBase();
signals:
/**
* Qt signal which is emitted right after receiving and handling the POSIX
* signal. In the Qt signal handler (slot) you are allowed to do anything.
*/
void receivedSignal();
protected:
static void staticSignalHandler(int (&sockets)[2]);
private slots:
void handleSignal();
private:
int (&sockets)[2];
QSocketNotifier *notifier;
};
// Actual proxy class, defining its own static proxy signal handler function and sockets.
template <int signum>
class PosixSignalProxy : public PosixSignalProxyBase
{
public:
PosixSignalProxy(QObject *parent = nullptr, int posixSignalFlags = 0) :
PosixSignalProxyBase(parent, mySockets, signum, &posixSignalHandler, posixSignalFlags)
{}
PosixSignalProxy(int posixSignalFlags) :
PosixSignalProxy(nullptr, posixSignalFlags)
{}
static void posixSignalHandler(int) {
PosixSignalProxyBase::staticSignalHandler(mySockets);
}
private:
static int mySockets[2];
};
template <int signum>
int PosixSignalProxy<signum>::mySockets[2];
#endif // POSIXSIGNALPROXY_H
posixsignalproxy.cpp
#include "posixsignalproxy.h"
#include <QSocketNotifier>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
PosixSignalProxyBase::PosixSignalProxyBase(QObject *parent, int (&sockets)[2], int posixSignalNumber, PosixSignalHandlerType posixSignalHandler, int posixSignalFlags) :
QObject(parent),
sockets(sockets)
{
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockets))
qFatal("PosixSignalProxy: Couldn't create socket pair");
notifier = new QSocketNotifier(sockets[1], QSocketNotifier::Read, this);
connect(notifier, SIGNAL(activated(int)), this, SLOT(handleSignal()));
struct sigaction sa;
sa.sa_handler = posixSignalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = posixSignalFlags;
if (sigaction(posixSignalNumber, &sa, 0) > 0)
qFatal("PosixSignalProxy: Couldn't register POSIX signal handler");
}
PosixSignalProxyBase::~PosixSignalProxyBase()
{
delete notifier;
}
void PosixSignalProxyBase::staticSignalHandler(int (&sockets)[2])
{
char tmp = 1;
::write(sockets[0], &tmp, sizeof(tmp));
}
void PosixSignalProxyBase::handleSignal()
{
notifier->setEnabled(false);
char tmp;
::read(sockets[1], &tmp, sizeof(tmp));
// Here, we're allowed to do Qt stuff.
emit receivedSignal();
notifier->setEnabled(true);
}
You're looking for QCoreApplication::aboutToQuit if you want to use a signal.
Better idea IMO is for engine not to have static functions but to manage its resources properly in its destructor. You can make it a singleton if you want.
I need your advice on a program I'm coding right now. Let me first present you what it is.
Design
I'm designing a man-machine interface (MMI). In this MMI, there are two core elements:
The MainWindow:
This MainWindow is the base of everything. Important: Here, I launch in a thread a server that receives the data from a client. This data is very important to the next element.
Supervision
This window contains a QTableWidget, the goal is to show in pseudo-real time the data received on the server in the thread of MainWindow.
The Problem
The server in the thread owned by the MainWindow receive a structure every 10 mS. How can I send these data to the Supervision window if it's open? I was thinking of using shared memory, but not really sure about this, and doesn't really which method I have to use.
Some Solutions
I tried to implement the solution of Sebastian Lange :
An emit in the thread Server
A connect in the MainWindow
A slot in supervision
So now my thread Server emit a signal at every frame received. But how can I do the connect in the MainWindow and how will Supervision receive the struct emit in the signal?
Here's the code of my emit:
MainWindow* MainWindow::m_psMainWindow = nullptr; // C++ 11 nullptr
void MainWindow::emit_signal_TrameRecu(StructureSupervision::T_StructureSupervision* ptr){
emit signal_TrameRecu(ptr);
}
void MainWindow::lancerServeur(std::atomic<bool>& boolServer){
serveur s;
StructureSupervision::T_StructureSupervision* bufferStructureRecu;
while(boolServer){
bufferStructureRecu = s.receiveDataUDP();
if(bufferStructureRecu->SystemData._statutGroundFlight != 0){
m_psMainWindow->emit_signal_TrameRecu( bufferStructureRecu );
}
}
}
Queued Connections
Qt makes cross thread communication easy when you use queued connections.
Your calls to connect should use the Qt::QueuedConnection or Qt::BlockingQueuedConnection connection types.
Custom Types in Slots and Signals
To use custom types (structs) in slots, signals, QVariant and properties you need to declare and register the type to make it available to the Qt dynamic type system.
In your header (.hpp) use Q_DECLARE_METATYPE and in your source (.cpp) use qRegisterMetaType.
Worked Example
server.hpp
#ifndef SERVER_HPP
#define SERVER_HPP
#include <QtCore>
struct customdata
{
int id;
QDateTime tstamp;
};
Q_DECLARE_METATYPE(customdata)
class Server : public QThread
{
Q_OBJECT
public:
Server();
signals:
void sendData(const customdata& d);
protected:
virtual void run();
};
#endif
server.cpp
#include "server.hpp"
static const int customdata_metatype_id =
qRegisterMetaType<customdata>();
Server::Server() : QThread() {}
void Server::run()
{
customdata d;
d.id = 0;
for (int i = 0; i < 10; ++i)
{
d.id++;
d.tstamp = QDateTime::currentDateTime();
emit sendData(d);
sleep(1);
}
}
window.hpp
#ifndef WINDOW_HPP
#define WINDOW_HPP
#include <QtGui>
#include "server.hpp"
class Window : public QWidget
{
Q_OBJECT
public:
Window();
public slots:
void receiveData(const customdata& d);
private:
QListWidget* mList;
};
#endif
window.cpp
#include "window.hpp"
Window::Window() : QWidget(),mList(new QListWidget())
{
resize(400, 300);
QVBoxLayout* mainLayout = new QVBoxLayout();
mainLayout->addWidget(mList);
setLayout(mainLayout);
}
void Window::receiveData(const customdata& d)
{
QString str(QString("%1 %2").arg(d.id).arg(d.tstamp.toString()));
mList->addItem(str);
}
main.cpp
#include <QtGui>
#include "server.hpp"
#include "window.hpp"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
Window win;
Server ser;
QObject::connect(
&ser, SIGNAL(sendData(customdata)),
&win, SLOT(receiveData(customdata)),
Qt::QueuedConnection);
win.show();
ser.start();
return app.exec();
}
test.pro
TEMPLATE=app
QT=core gui
HEADERS=server.hpp window.hpp
SOURCES=main.cpp server.cpp window.cpp
I once (in the days of Qt 3.2) implemented this cross thread communication using QApplication::postEvent (now QCoreApplication::postEvent). However nowadays best practise is to use promises and futures to communicate asynchronously between threads. Promises and futures has become a part of recent C++ standard and the concepts are also implemented separately as part of Qt 5 Concurrent framework. See http://qt-project.org/doc/qt-5/qtconcurrent-index.html
I would like to run a timer inside a QThread. I have written some code in which i am getting some error during the run time. Please guide me into the right direction. What am I doing wrong?
(Parent is QThread(0x1498d10), parent's thread is QThread(0x11272b0), current thread is QThread(0x1498d10)
mainwindow.h //main .h file
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mythread.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
MyThread *myt;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp //main .cpp file
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myt=new MyThread();
myt->start();
MainWindow w;
}
MainWindow::~MainWindow()
{
delete ui;
}
mythread.h // class for thread
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>
class MyThread:public QThread
{
public:
MyThread();
void run();
QTimer *thr;
public slots:
void slo();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread()
{
thr= new QTimer();
connect(thr,SIGNAL(timeout()),this,SLOT(slo()));
}
void MyThread::run()
{
thr->start(1000);
}
void MyThread::slo()
{
int i,j=0;
i=i+j;
}
Just my humble opinion - Do not to subclass QThread anymore, when you do not need to.
I think, you just want to run your class in new thread or more probably you do not want to block other tasks. Your class is not thread itself. Subclassing basically means that your class IS what you are subclassing.
In other words: Let QThread do its job and concentrate on your class to do what it should do.
Example: MyClass itself does not know anything about threads. It just do what it has to do. Incrementing value and showing results ( plus some sleep part to show how it can block other functions or gui )
Header file
#include <QTimer>
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(bool willSleep, QString name, QObject *parent = 0);
public slots:
void updateCount();
private:
QTimer *timer;
int count;
bool m_wantToSleep;
};
Implementation
#include "myclass.h"
#include <QDebug>
MyClass::MyClass(bool wantToSleep, QString name, QObject *parent) :
QObject(parent)
{
this->setObjectName(name);
m_wantToSleep = wantToSleep;
count = 0;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateCount()));
timer->start(100);
}
void MyClass::updateCount()
{
++count;
qDebug() << objectName() << " count: " << count;
if (m_wantToSleep)
sleep(1);
}
We have code which does the job.
Now implement more threads - its very simple ( memory management, etc not handled to have simple example )
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QThread *thread1 = new QThread; // First thread
QThread *thread2 = new QThread; // Second thread
thread1->start();
thread2->start();
MyClass *myClass = new MyClass(false, "normal class");
MyClass *mySleepClass = new MyClass(true, "sleeper class");
// Better to implement start slot to start timer ( not implemented )
// connect(thread1, SIGNAL(started), myClass, SLOT(start()));
// but this suffice, because timer will emit first signal after class is moved to another thred
//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);
}
MainWindow::~MainWindow()
{
delete ui;
}
Now we can play with threads:
Blocking GUI ( of course we do not want this )
Initial example works without using new threads. Objects are in current thread and that's why GUI will be blocked. ( since I use sleep function in one instance )
//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);
Non blocking GUI
We have two more threads running. Why not to use them. In example QThreads are already running, but they play with nothing. Let's move our instances there, to ensure main loop, where GUI is living will not be blocked anymore.
Magic function is moveToThread
Uncomment lines and you can see, that GUI will not be blocked. Both instances are in new thread. But then again, there is a sleep function so One should be counting faster then other. But it is not. Because they are blocking each other. They are in one thread.
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread1);
Results in both previous cases should be: ( instances lives in same thread and shares the same event loop, so they are blocking each other )
"normal class" count: 1
"sleeper class" count: 1
"normal class" count: 2
"sleeper class" count: 2
"normal class" count: 3
"sleeper class" count: 3
So move them to separate thread
Now GUI is not blocked, niether instances each other.
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread2);
Results should be: ( and GUI should not be blocked )
"sleeper class" count: 1
"normal class" count: 1
"normal class" count: 2
"normal class" count: 3
"normal class" count: 4
"normal class" count: 5
Hope It was understandable. As for me, this is more logic aproach then subclassing.
Of course you can create QThread in your MyClass, it is not necessary to create it oustide MyClass, I just wanted to show, that you can create one thread and move there more instances.
For anyone who disagree, I just wanted to say that: MyClass is counter with thread support sounds better then: MyClass is thread with counter ability :)
Your timer does not belong to your thread. You should create it in your run() method or you should call tmer->moveToThread before connecting it to slots, but after thread was started.
Check it: MyThread belongs to your main thread. You create timer in constructor of MyThread - so timer belongs to main thread too. But you are trying to initialize and use it in ::run method, that belongs to other thread.
In order to do this, you need to have event loop in your thread.
From QTimer's man page:
In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.
From QThread's man page:
int QThread::exec () [protected]
Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit().
It is necessary to call this function to start event handling.
Also, you need to have Q_OBJECT in your class:
class MyThread:public QThread
{
Q_OBJECT
And finally, as Dmitry noted, you need to create QTimer inside your thread, so the entire cpp file should look like this:
#include "mythread.h"
MyThread::MyThread()
{
}
void MyThread::run()
{
thr= new QTimer();
connect(thr,SIGNAL(timeout()),this,SLOT(slo()));
thr->start(1000);
exec();
}
void MyThread::slo()
{
int i = 0,j=0;
i=i+j;
}
Also, read this document.
I was able to create a simple example that starts a timer within another thread, using lambda functions. Here is the code:
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QThread* thread = new QThread(&app);
QObject::connect(thread, &QThread::started, [=]()
{
qInfo() << "Thread started";
QTimer* timer1 = new QTimer(thread);
timer1->setInterval(100);
QObject::connect(timer1, &QTimer::timeout, [=]()
{
qInfo() << "Timer1 " << QThread::currentThreadId();
});
timer1->start();
});
thread->start();
QTimer timer2(&app);
QObject::connect(&timer2, &QTimer::timeout, [=]()
{
qInfo() << "Timer2 " << QThread::currentThreadId();
});
timer2.setInterval(100);
timer2.start();
return app.exec();
}