How to queue lambda function into Qt's event loop?
Your problem is of How to leverage Qt to make a QObject method thread-safe? Let's adapt the solutions offered there to your use case. First, let's factor out the safety check:
bool isSafe(QObject * obj) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
auto thread = obj->thread() ? obj->thread() : qApp->thread();
return thread == QThread::currentThread();
}
The approach you suggested takes a functor, and lets the compiler deal with packing up the arguments (if any) within the functor:
template <typename Fun> void postCall(QObject * obj, Fun && fun) {
qDebug() << __FUNCTION__;
struct Event : public QEvent {
using F = typename std::decay<Fun>::type;
F fun;
Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
~Event() { fun(); }
};
QCoreApplication::postEvent(
obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}
A second approach stores the copies of all the parameters explicitly within the event and doesn't use a functor:
template <typename Class, typename... Args>
struct CallEvent : public QEvent {
// See https://stackoverflow.com/a/7858971/1329652
// See also https://stackoverflow.com/a/15338881/1329652
template <int ...> struct seq {};
template <int N, int... S> struct gens { using type = typename gens<N-1, N-1, S...>::type; };
template <int ...S> struct gens<0, S...> { using type = seq<S...>; };
template <int ...S> void callFunc(seq<S...>) { (obj->*method)(std::get<S>(args)...); }
Class * obj;
void (Class::*method)(Args...);
std::tuple<typename std::decay<Args>::type...> args;
CallEvent(Class * obj, void (Class::*method)(Args...), Args&&... args) :
QEvent(QEvent::None), obj(obj), method(method), args(std::move<Args>(args)...) {}
~CallEvent() { callFunc(typename gens<sizeof...(Args)>::type()); }
};
template <typename Class, typename... Args> void postCall(Class * obj, void (Class::*method)(Args...), Args&& ...args) {
qDebug() << __FUNCTION__;
QCoreApplication::postEvent(
obj->thread() ? static_cast<QObject*>(obj) : qApp, new CallEvent<Class, Args...>{obj, method, std::forward<Args>(args)...});
}
It's used as follows:
struct Class : QObject {
int num{};
QString str;
void method1(int val) {
if (!isSafe(this))
return postCall(this, [=]{ method1(val); });
qDebug() << __FUNCTION__;
num = val;
}
void method2(const QString &val) {
if (!isSafe(this))
return postCall(this, &Class::method2, val);
qDebug() << __FUNCTION__;
str = val;
}
};
A test harness:
// https://github.com/KubaO/stackoverflown/tree/master/questions/safe-method-40382820
#include <QtCore>
// above code
class Thread : public QThread {
public:
Thread(QObject * parent = nullptr) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
void moveToOwnThread(QObject * obj) {
Q_ASSERT(obj->thread() == QThread::currentThread());
auto thread = new Thread{obj};
thread->start();
obj->moveToThread(thread);
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
Class c;
moveToOwnThread(&c);
const auto num = 44;
const auto str = QString::fromLatin1("Foo");
c.method1(num);
c.method2(str);
postCall(&c, [&]{ c.thread()->quit(); });
c.thread()->wait();
Q_ASSERT(c.num == num && c.str == str);
}
Output:
postCall
postCall
postCall
method1
method2
The above compiles and works with either Qt 4 or Qt 5.
See also this question, exploring various ways of invoking functors in other thread contexts in Qt.
How can I have QThread emit a heap-allocated QObject without leaking?
Use smart pointer (e.g., QSharedPointer
) instead a normal pointer:
DataClass *result = new DataClass;
should be replaced with
QSharedPointer<DataClass> result = QSharedPointer<DataClass>(new DataClass);
Then, you could safely pass it somewhere and do not worry about deleting it. When it is out of the last scope where it can be used, the object will be automatically destroyed.
Qt thread ID is equal to MainWindows? (moveToThread)
There are several issues that I'll address in random order.
First of all, using thread IDs is bad user experience. Give the threads a descriptive name:
int main(...) {
QApplication app(...);
QThread myThread;
MyObject myObject;
myObject->moveToThread(&myThread);
QThread::currentThread()->setObjectName("mainThread");
myThread.setObjectName("myThread");
...
}
Then use QThread::currentThread()->objectName()
to retrieve it. You can also pass QObject*
to qDebug()
to display the name of the thread:
qDebug() << QThread::currentThread();
Your signal invocation would then become:
QString currentThreadName() {
return QThread::currentThread()->objectName().isEmpty() ?
QStringLiteral("0x%1").arg(QThread::currentThread(), 0, 16) :
QThread::currentThread()->objectName();
}
...
emit requestFileContent(
QStringLiteral("Emitting from thread \"%1\"\n").arg(currentThreadName));
Then, use the above to deal with the thread you've created:
auto thread = new QThread(this);
thread->setObjectName("fileThread");
ui.textBrowser->append(QStringLiteral("Worker thread: \"%1\").arg(thread->objectName()));
auto AVC_file = new AVC_File;
AVC_file->moveToThread(thread);
...
But AVC_FileCheck
is invoked from the main thread. Whether that's OK or not depends on how that method is implemented. It needs to be thread-safe, see this question for a discussion of that. TL;DR: The following pattern could be a starting point:
class AVC_file : public QObject {
Q_OBJECT
Q_SLOT void fileCheck_impl(QIODevice * dev) {
dev->setParent(this);
...
}
Q_SIGNAL void fileCheck_signal(QIODevice *);
public:
void fileCheck(QIODevice *dev) { fileCheck_signal(dev); }
AVC_file(QObject *parent = nullptr) : QObject(parent) {
connect(this, &AVC_file::fileCheck_signal, this, &AVC_file::fileCheck_impl);
...
}
};
Finally, your existing AVC_fileCheck
API is broken. You pass QFile
by reference: this won't ever work since it ceases to exist as soon as on_OpenAVCFile_clicked
returns. When AVC_file
uses that file in its thread, it's a dangling object reference.
Instead, you must pass the ownership of the file to AVC_file
, and pass a pointer to an instance that AVC_file
will dispose when done with. Or simply let AVC_file
open the file for you!
Why is a slot being called in receiver's thread even after using Qt::DirectConnection? How do I ensure that it is called in the other thread?
You have to consider 3 threads in your problem statement:
- The thread the receiver lives in
- The thread the sender lives in
- The thread that is emitting the signal
Queued/BlockingQueued connections ensure that the slot will be executed in the receivers thread.
DirectConnection executes the slot in the current thread. This is not always the thread the sender lives in! In fact, there is no standard way to enforce running a slot in the sender's thread (because usually, the receiver's thread is what you want).
NB: AutoConnection uses QueuedConnection if the current thread and receiver thread are not the same, otherwise DirectConnection
To solve your problem, you could forcably switch to the sender's thread if you're on another thread, like this:
In EmitCaller, add
private: Q_INVOKABLE my_thread_do_emit() { do_emit(); }
Then in the implemetation:
void EmitCaller::do_emit()
{
if (this->thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "my_thread_do_emit", Qt::BlockingQueuedConnection);
} else {
emit my_signal();
}
}
However, I would suggest that you reconsider your design. It seems unusual to call a slot in a certain foreign thread. Maybe there is a problem in your thread affinity setup... (e.g. the receiver should live in the newly created thread)
Qt 5: emit signal from non-Qt thread
The error message tells you exactly what you need to know: you can't use the event loop system without QCoreApplication
existing. That's all. All of your exploration into the innards of Qt was educational, but a red herring. None if it matters at all.
only the receiver thread has to have the Qt event loop that will dispatch the event from the queue
That's correct.
Does it mean that if I create QCoreApplication inside QThread, this system should work?
You might create it on any thread (in contrast to QGuiApplication that can only live on the main thread). But make sure that you link statically with Qt. Otherwise, if you're linking with system Qt, you'll become binary incompatible with any process using the same Qt if you create a second instance of the application. Thus, if you use system Qt you can work around by inspecting whether an application instance exists, and only create one if it doesn't exist yet.
Furthermore, you shouldn't really need to create the application instance in a custom thread. Your library should accept an initialization call that should be performed in the main thread of the calling process. This initialization can create an application object if one doesn't exist.
// https://github.com/KubaO/stackoverflown/tree/master/questions/twothreads-41044526
#include <QtCore>
// see http://stackoverflow.com/questions/40382820
template <typename Fun> void safe(QObject * obj, Fun && fun) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
if (Q_LIKELY(obj->thread() == QThread::currentThread()))
return fun();
struct Event : public QEvent {
using F = typename std::decay<Fun>::type;
F fun;
Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
~Event() { fun(); }
};
QCoreApplication::postEvent(
obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}
class Worker : public QObject {
Q_OBJECT
QBasicTimer m_timer;
int n = 0;
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == m_timer.timerId())
emit hasData(n++);
}
public:
Q_SIGNAL void hasData(int);
Q_SLOT void onData(int d) { qDebug() << QThread::currentThread() << "got data" << d; }
void start() {
safe(this, [this]{ m_timer.start(50,this); });
}
void quit() {
safe(this, [this]{ m_timer.stop(); thread()->quit(); });
}
};
class Library {
QByteArray dummy{"dummy"};
int argc = 1;
char *argv[2] = {dummy.data(), nullptr};
QScopedPointer<QCoreApplication> app;
static Library *m_self;
struct {
Worker worker;
QThread thread;
} m_jobs[3];
public:
Library() {
Q_ASSERT(!instance());
m_self = this;
if (!qApp) app.reset(new QCoreApplication(argc, argv));
for (auto & job : m_jobs) {
job.worker.moveToThread(&job.thread);
job.thread.start();
job.worker.start();
QObject::connect(&job.worker, &Worker::hasData, &m_jobs[0].worker, &Worker::onData);
}
}
~Library() {
for (auto &job : m_jobs) {
job.worker.quit();
job.thread.wait();
}
}
static Library *instance() { return m_self; }
};
Library *Library::m_self;
// API
void initLib() {
new Library;
}
void finishLib() {
delete Library::instance();
}
int main()
{
initLib();
QThread::sleep(3);
finishLib();
}
#include "main.moc"
Background thread with QThread in PyQt
I created a little example that shows 3 different and simple ways of dealing with threads. I hope it will help you find the right approach to your problem.
import sys
import time
from PyQt5.QtCore import (QCoreApplication, QObject, QRunnable, QThread,
QThreadPool, pyqtSignal)
# Subclassing QThread
# http://qt-project.org/doc/latest/qthread.html
class AThread(QThread):
def run(self):
count = 0
while count < 5:
time.sleep(1)
print("A Increasing")
count += 1
# Subclassing QObject and using moveToThread
# http://blog.qt.digia.com/blog/2007/07/05/qthreads-no-longer-abstract
class SomeObject(QObject):
finished = pyqtSignal()
def long_running(self):
count = 0
while count < 5:
time.sleep(1)
print("B Increasing")
count += 1
self.finished.emit()
# Using a QRunnable
# http://qt-project.org/doc/latest/qthreadpool.html
# Note that a QRunnable isn't a subclass of QObject and therefore does
# not provide signals and slots.
class Runnable(QRunnable):
def run(self):
count = 0
app = QCoreApplication.instance()
while count < 5:
print("C Increasing")
time.sleep(1)
count += 1
app.quit()
def using_q_thread():
app = QCoreApplication([])
thread = AThread()
thread.finished.connect(app.exit)
thread.start()
sys.exit(app.exec_())
def using_move_to_thread():
app = QCoreApplication([])
objThread = QThread()
obj = SomeObject()
obj.moveToThread(objThread)
obj.finished.connect(objThread.quit)
objThread.started.connect(obj.long_running)
objThread.finished.connect(app.exit)
objThread.start()
sys.exit(app.exec_())
def using_q_runnable():
app = QCoreApplication([])
runnable = Runnable()
QThreadPool.globalInstance().start(runnable)
sys.exit(app.exec_())
if __name__ == "__main__":
#using_q_thread()
#using_move_to_thread()
using_q_runnable()
Related Topics
How to Store Extremely Large Numbers
How to Separate C++ Main Function and Classes from Objective-C And/Or C Routines at Compile and Link
How to Know If a Pointer Points to the Heap or the Stack
Allocating More Memory Than There Exists Using Malloc
Detect 32-Bit or 64-Bit of Windows
Std::Sort Does Not Always Call Std::Swap
How to Use Visual Studio Code to Compile Multi-Cpp File
Order of Evaluation of Assignment Statement in C++
C++ Return Value Without Return Statement
How to Clone as Derived Object in C++
Are Multiple Mutations Within Initializer Lists Undefined Behavior
How to Leverage Qt to Make a Qobject Method Thread-Safe
Understanding Boost.Spirit's String Parser
Extracting C/C++ Function Prototypes
Why Does Calling Std::String.C_Str() on a Function That Returns a String Not Work
Variable Assignment in an "If" Condition
Is It a Good Idea to Return " Const Char * " from a Function