I wish to execute code from a lambda asynchronously while keeping an eventloop running and I figured: this could work...
auto thread = QSharedPointer<QThread>(new QThread);
QEventLoop l;
connect( thread.data(), &QThread::finished, &l, &QEventLoop::quit );
connect( thread.data(), &QThread::started, [=]() {
for(int i=0; i<100; ++i ) {
qDebug() << "waiting... " << i;
}
QThread::currentThread()->sleep(10);
} );
thread->start();
l.exec();
auto const fin = thread->wait();
qWarning() << fin;
Everything is working as expected but: I doesn't get the part when the thread finishes its lambda function. It seems finished
is not emitted and wait
(even without the extra eventloop) will block forever.
How do I get the event loop to exit or the wait to return? Or this there a better way to let a lambda run in another thread and wait for it with an non-blocked event loop?
Thank you
There are two issues:
The connect
from QThread::started
to the lambda lacks the object context for the given thread. Because of that, the lambda will be executed in the current thread (specifically in thread->thread()
and that's not same as thread
!).
You never quit
the thread.
Object context is not the same as the thread. You need a QObject
that lives in a given thread; it can't be the thread itself. A QThread
is really a thread handle and isn't meant to live in its own thread (and it doesn't!). Resist the urge to move a QThread
instance to its thread: you then have a sad case of a handle that lives in the thread it manages. As soon as the thread ends, the handle becomes non-functional since it moves to a null thread.
Side notes:
QThread::sleep
is static.QEventLoop::quit
is thread-safe, even if it's not documented to be so. You could quit from the lambda, saving one connect.QThread thread;
QEventLoop loop;
QObject context;
context.moveToThread(&thread);
connect(&thread, &QThread::started, &context, [&]() {
qDebug() << "waiting... ";
QThread::sleep(10);
qDebug() << "done";
loop.quit();
});
thread.start();
loop.exec();
thread.quit();
thread.wait();
// some other code
Alas, this leads to reentering the event loop and spaghetti code: the world is asynchronous. There's nothing guaranteeing that the method where you run all this won't be reentered from the event loop. Instead, you should return control to the base event loop. You should also leverage the thread pool so as not to manage your threads manually. Creation of a thread is expensive, transient threads are an anti-pattern and a premature pessimization. Of course perhaps your shared thread was meant to be reused, but even then you're very likely to underutilize it. The global thread pool instance has global insight into the needs of your whole application and is better positioned to manage thread lifetime.
void doFirst() {
QtConcurrent::run([this]{
qDebug() << "waiting...";
QThread::sleep(10);
qDebug() << "done";
QObject src;
src.connect(&src, &QObject::destroyed, this, [this]{ doNext(); });
// see https://stackoverflow.com/q/21646467/1329652 for better ways
// of invoking doNext
});
}
void doNext() {
// some other code
}
For nicer ways of executing code in a given thread/object context, see this question.
If your lambdas are I/O bound, you should use a custom, larger thread pool for them (and only for them). QtConcurrent::run
can take your thread pool as the first argument since Qt 5.4.