What Is an Event Loop in Qt

What is an event loop in Qt?

Now, when we say event loop, does it mean that there is some while
loop running in the internal code of Qt, and in that while loop the
method of handling signals and slots is written?

In a sense, yes. Most software these days sits and waits for events -- user input, network traffic, timer events, sensors, etc. -- and responds accordingly.

This is not specific to Qt. It's a common design pattern you'll find everywhere from Windows to Android to Arduino.

What is the meaning of the term event loop w.r.t threads in Qt?

An event loop is usually a loop that is run by the main thread to receive events that either originate from the system (e.g. GUI interaction, network events, timers, ...) or from other Qt components (e.g. QCoreApplication::postEvent(), ...). The event loop waits for new events to arrive in the event queue, then takes them out of the queue and sends them to their destination QObject where they are handled by an overridden QObject::event(QEvent*) (e.g. A QPushButton would handle a mouse press event by emitting pressed(), ...).

Per-thread event loops are a generalization of the concept above. This makes it possible to handle events in worker threads by introducing the concept of a QObject's thread affinity. Thread affinity is the thread in which a particular QObject should have its events handled (the thread from which QObject::event gets called for this QObject). In the big picture, this can be used to run asynchronous code in worker threads (since GUI code should be run only in the main thread). For example, you can run many asynchronous sockets and have QTimers to disconnect these sockets after some specified time of inactivity all in a single worker thread. Per-thread event loops are also essential for cross-thread signal-slot connections, since this kind of signal emissions is translated into QMetaCallEvents under the hood (to be delivered to its destination QObject).

Event loops and signal-slot processing when using multithreading in Qt

All results you got are perfectly correct. I'll try to explain how this works.

An event loop is an internal loop in Qt code that processes system and user events. Event loop of main thread is started when you call a.exec(). Event loop of another thread is started by default implementation of QThread::run.

When Qt decides it's time to process an event, it executes its event handler. While event handler is working, Qt has no chance to process any other event (unless given directly by QApplication::processEvents() or some other methods). Once the event handler is finished, control flow returns to the event loop and Qt may execute another handler to process another event.

Signals and slots are not the same as events and event handlers in Qt terminology. But slots are handled by event loops somewhat similarily. If you have control flow in your code(such as in main function) you can execute any slot immediately just as any other C++ function. But when Qt does that it can only do that from an event loop. It should be noted that signals are always sent immediately, while slot execution may be delayed.

Now let's see what happens in each case.

Case 1

WorkerManager::process is executed directly at the program start. New thread is started and Worker::process is executed immediately in the new thread. WorkerManager::process continues execution until Worker is done, freezing all other actions (including slot processing) in main thread. After WorkerManager::process is finished, control flow goes to QApplication::exec. Qt establishes connection to the other thread, receives messages about slot invokation and invokes all of them consequently.

Case 2

Qt by default executes slots of an object in the thread this object belongs to. Main thread will not execute slots of WorkerManager because it belongs to another thread. However this thread is never started. Its event loop is never finished. Invokations of slot1 and slot2 are left forever in Qt's queue waiting for you to start the thread. Sad story.

Case 3

In this case WorkerManager::process is executed in main thread because you invoke it directly from main thread. Meanwhile, WorkerManager's thread is started. Its event loop is launched and waiting for events. WorkerManager::process starts Worker's thread and executes Worker::exec in it. Worker starts sending signals to WorkerManager. WorkerManager's thread almost immediately starts to execute appropriate slots. At this point it seems awkward that WorkerManager::slot2 and WorkerManager::process are executed simultaneously. But it's perfectly fine, at least if WorkerManager is thread-safe. Shortly after Worker is done, WorkerManager::process is finished and a.exec() is executed but has not much to process.

Case 4

Main function just launches WorkerManager's thread and immediately goes to a.exec(), resulting in end as first line in the output. a.exec() processes something and ensures program execution but doesn't execute WorkerManager's slots because it belongs to another thread. WorkerManager::process is executed in WorkerManager's thread from its event loop. Worker's thread is started and Worker::process starts sending signals from Worker's thread to WorkerManager's thread. Unfortunately the latter is busy executing WorkerManager::process. When Worker is done, WorkerManager::process also finishes and WorkerManager's thread immediately executes all queued slots.

The largest problem in your code is usleep and infinite loops. You should almost never use those when working with Qt. I understand that a sleep in Worker::process is just a placeholder for some real calculation. But you should remove sleep and infinite loop from WorkerManager. Use WorkerManager::slot1 to detect Worker's termination. If you develop a GUI application there would be no need to move WorkerManager to another thread. All its methods (without sleep) will be executed fast and will not freeze the GUI.

QT event loop in a dll

The native application should be spinning a native event loop in the main thread. Qt uses the native event loop on most platforms, so you don't have to use QCoreApplication::exec() and block there to dispatch events. Instead, to have a decent cross-platform main thread event loop integration just "prime" the event loop by letting it spin once. This ensures that Qt is ready to have its events dispatched by whoever runs the native event loop on a given thread (here: main thread).

It is non-portable to instantiate QApplication on any thread but the main thread. It happens to work on Windows, but it won't work on OS X at all, and whether it works on X11 depends on what exact platform implementation you're integrating with.

static std::unique_ptr<QApplication> app;
static int argc{1};
static const char * argv[] = { "myLibrary", nullptr };

void myLibraryInit() {
app.reset(new QApplication{argc, argv});
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
app.exec();
}

void myLibraryDeInit() {
app.reset();
}

At that point, you're free to start any QThreads that spin their own event loops and do whatever else that's needed. You have to make sure that any database access objects are created in the thread where they'll be used.

Threads & Event loop in the Qt application

Each thread processes its own event loop, you normally do not need to worry about this - its taken care of for you and unless you have a specific reason to its meant to be left alone.

QThread is a class provided by Qt for you to use to control the operation of a thread. The method of "putting" object into that thread is to use the moveToThread() function.

You should not inherit the QThread class in order to run some code inside a thread (use the moveToThread function), the only reason to inherit the QThread class is if you want to change the behaviour of the thread control.

Below are the basic steps to get an object running inside a thread:

MyObj *myObj = new MyObj(0); // 0 = no parent if your object inherits QObject
QThread* thread = new QThread;
myObj->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), myObj, SLOT(run()));
thread->start();

Once you call start() the thread will start and emit the started signal, your object will receive it and process it in its slot/function run().

Note: your thread does not end when the function/slot run() inside your object ends (so you do not need to do a "forever" loop). The thread only stops when you tell it to quit (or destroy it), this means your thread can be idle until it receives a signal or event - this is where the event loop comes in - incoming events are handled by the event loop within the QThread class.

Note: also this code is a snippet - it does not deal with the shutting down of the thread, there are other "template" bits of code that you can use for this.

Edit

So events are handled by the event queue (things like mouse click events all of base type QEvent) - used more by the system where some events may trigger signals (onClicked for example). Signals and slots are a different mechanism that is used more by the user where you handle these in your slots using the connect() function. Here is a better explanation then I could come up with:
see here

QT Eventloop and slot handle?

I found the reason, may be this is helpful for someone

QTimer::singleShot(timeout, &extQObject10, &ExtentQObject::onExtentQObjectFirstSlot);

In this case QTimer::singleShot call to this overloaded function

QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj)
: QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(r), receiver(r), slotObj(slotObj)
{

timerId = startTimer(msec, timerType);
if (r && thread() != r->thread()) {
// Avoid leaking the QSingleShotTimer instance in case the application exits before the timer fires
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
setParent(0);
moveToThread(r->thread());
}
}

this created a timer instance (let call timerA) and because extQObject10, which is receiver set for QTimer::singleShot, is moved to extThread1 so timerA is moved there too.

Because QTimer extended QObject so it inherited bool QObject::event(QEvent *e) from QObject which handle thread change as below

    case QEvent::ThreadChange: {
Q_D(QObject);
QThreadData *threadData = d->threadData;
QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load();
if (eventDispatcher) {
QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
if (!timers.isEmpty()) {
// do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
eventDispatcher->unregisterTimers(this);
QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,
Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers))));
}
}
break;

Here, QMetaObject::invokeMethod results a slot handle by extQThread1, so it is will be handled after loopTimeoutMsec which is set to extQThread1. After that, the timerA start running and will fire after the timeout set for it, at that time onExtentQObjectFirstSlot will be called on extObject10.

In summary, onExtentQObjectFirstSlot will be called on extObject10 after loopTimeoutMsec (set for extQThread1) + timeout (set for QTimer::singleShot)

Qt blocked event loop

I don't think there is any "magic" solution to be found here; a thread can't be running Qt's event loop if it is running your own custom event loop. In practice, there are two common solutions, which are really two sides of the same coin:

  1. Call processEvents() periodically from your event loop, as you suggested in your question, so that the Qt event-handling code occasionally gets to run and handle incoming asynchronous signals.

  2. Don't have a long-running loop in your doWork() method. Instead, do a short amount of work, store the results/state of that work in a member variable or somewhere, and then call something like QTimer::singleShot(0, this, SLOT(doWork())) so that the Qt event loop will call your doWork() method again soon after the first call to doWork() returns. That way the Qt event loop never gets held off for longer than the (brief) period of time taken up by a single doWork() call.

Of those two options, I think the second is preferable, because it allows the Qt event loop to run in its normal fashion, and it also avoids a potential tripping-over-your-own-shoelaces issue -- e.g. imagine if while using solution (1) your call to processEvents() causes a slot to be called that deletes the BarWorker object. When the processEvents() call returns, BarWorker::doWork() will resume executing, but at that point, all of the local member variables and virtual methods it might access as part of its normal execution have been destroyed, and reading or writing them will cause undefined behavior (if you're lucky, an easy-to-debug crash). That possible snafu can't happen when using solution (2), since if the BarWorker object gets deleted between calls to doWork(), any queued-up asynchronous call to doWork() will be safely cancelled.



Related Topics



Leave a reply



Submit