Qt Thread Slots

2021年5月30日
Register here: http://gg.gg/us0a7
*Qt Slots Thread Safe
*Qt Thread Slots Online
*Qthread Slot Connection
The event loops of Qt are a very valuable tool for inter-thread communication. Every thread may have its own event loop. A safe way of calling a slot in another thread is by placing that call in another thread’s event loop. This ensures that the target object finishes the method that is currently running before another method is started. Qt’s event systemis very useful for inter-thread communication. Every thread may have its own event loop. To call a slot (or any invokablemethod) in another thread, place that call in the target thread’s event loop.
There are basically two different ways of using QThread directly: Worker threads or subclassing. Those two are confusing to a lot of developers, and I will try to explain when you should use those. I will also explain how signals and slots work with threads and how they can help you or lead to problems.
There are other ways to use QThread than those two. There are hybrid cases that lie somewhere between the two general classes, and you can use QThreadPool or the QtConcurrent. Those cases will not be described here, but I might come back to those in later blog posts.
When you look at the discussion on whether or not to subclass QThread, you will find a lot of people who claim this is bad. For example the famous “You’re doing it wrong” blog post from 2010 (yes, this is an old discussion). I have mixed feelings about this blog post and the arguments it presents. On one hand, I actually encourage developers to follow the recommendations in this article, because I agree that this is the path that leads to fewer errors in your code. OTOH, there are certainly valid use cases for subclassing.
The fundamental problem here is that most developers do not really understand the thread model of Qt. And in some cases they do not even understand threading at all. There are a lot of developers in this industry who should not touch multithreaded code at all, but still have tasks in their daily job that force them to do this. I won’t try to educate general thread development here, but I will encourage everyone who needs to learn this to read a good book on multithreaded development. I’m only going to talk about the QThread class here and assume you understand threading.
So let’s get started on the issue at hand.
There are two Qt technologies that you need to understand before you can understand why QThread works the way it does.QObject thread affinity
The first of those two technologies is thread affinity. Each QObject has a thread that it belongs to. Unless you set this manually, QObject chooses the current thread when the constructor is run.
Thread affinity has a bunch of subtle consequences for our objects, once you start going multithreaded. Of course, if you don’t have any threads, the objects all live in the main thread, and then you don’t have to worry about it. But with threading, you do have to worry about it.
The first thing you have to know is what this means for signals and slots.
When you do a connect from a signal to a slot, you have a choice of connection type. Those can be automatic, direct or queued. Yes, there are a couple of others, but those are not interesting from a threading point of view.
With a direct connection, the slot will be called by the current thread that is emitting the signal. Usually, this is very bad, if the two objects live in different threads. There are cases where you can do this – one example could be a slot that locks a mutex, adds something to a queue, unlocks and returns. But you have a problem if the object can be deleted by another thread while your slot is being executed, of course. My recommendation is not to use this unless you absolutely have to, you really know what you are doing, and make sure you document both sides very precisely.
For queued connections to work, the thread where the receiving object lives (i.e. has the thread affinity), must have an active event loop. This is not necessary for the sender. What happens is that the sending signal will create an event with the arguments of the signal and send this event to the receiver thread eventloop. In the eventloop, the event is transformed to a normal slot call. This happens no matter if the two objects live in the same thread or if they are in different threads. (And this is one of the simplest ways to send information from one thread to another.)
Automatic connection does a check in the signal whether the thread affinity of the receiver is the same as the current thread. If these are the same, it does a direct signal to slot call. If they are different, this is handled as a queued connection. This check is done every time the signal is emitted, which means the thread of the signal is irrelevant and the connect just works. Note that the signalling object does not have to live in a thread with an event loop.QThread thread affinity
The second thing you need to understand, is the thread affinity of the QThread object. This is the part that confuses a lot of developers, although the rules are actually quite simple.
The thread affinity of a QThread object is the creating thread. It does not live in the thread that it models and implements. This may sound counterintuitive, until you actually think about this.
First, when you create the QThread object, the new thread doesn’t exist. This means the constructor will necessarily have to run in the thread that creates it. Also, when the thread is finished and you want to delete it, this will be done in the creating thread as well, so the destructor runs in the creating thread.
However, in the run method of QThread, you are now in the new thread.
This means when you create objects in a QThread subclass method, they will have different thread affinity based on the instantiation time. If you create an object in the constructor (or in the destructor, but that’s rare) this object will have the calling thread affinity. If you create an object in the run method, this will have the new thread affinity. If you have a method on the QThread subclass, an object created in here will (as usual) have the current thread affinity – so if you call it from the constructor it will have the creating thread affinity, and if you call it from run it will have the new thread affinity.
For signals, the thread affinity doesn’t matter. But for slots it matters a lot.Subclassing QThread
Now you can perhaps appreciate why subclassing QThread can be a problem. If you have anything using slots in the subclass objects, the thread affinity will mean that it will use the original thread. This is also the case for lambda slots or what other tricks you could come up with.
So, to “fix” this, developers try various workarounds. First, they use moveToThread(this) on the thread object. Unfortunately, this is probably the worst thing you can do, because that is a violation of the basic assumption of QThread itself. Others will connect using the DirectConnection flag, which actually works, but is a very brittle solution that probably will break while people are working on the code in the years to come.
The next problem you have to consider is when you instantiate other objects from the thread object itself. If you want an object to live in the new thread, you must instantiate it inside the run method and not set the thread object as the parent, because Qt does not like the parent-child relationship to go across from one thread to another.Recommendations
So, now we’re finally ready for a list of what you should do in different cases.
If you have a task you need to run in a QThread, this is the place where you do as the QThread documentation says, and create a worker thread. Just instantiate a QThread object, instantiate the objects to live in this thread, call moveToThread(thread) on those objects and start the thread. This is what you should do for most cases.
If you have something that’s just a pure calculation or something that connects to hardware, then you will often use a QThread subclass. Yes, you can implement this using worker threads as well, but that’s usually a silly way to do it. (For the pure calculation case, I would actually use QtConcurrent or the QThreadPool, but that’s a topic for a later blog post.)
When you look at this recommendation, there are two things that are the defining questions:
*Do you need slots?
*Do you need QObject instances in the new thread?
If the answer to one of those questions is yes, then you should follow the standard recommendation and use a worker thread. If the answer is no, you have a choice between subclassing and worker threads.
If you answer yes to either of those questions and still choose subclassing, you are probably going to have issues that are really hard to find. Remember, there’s actually a reason people suggest that you don’t subclass QThread. If you do it, you expose yourself to a couple of sets of problems that you don’t have when you use a worker thread.
Effective Threading Using Qt
Over the years using Qt I’ve seen a lot of difficulty using threads with Qt.Threads can be difficult and Qt provides a lot of ways to make threads easy towork with. Still basic / direct / low level threading (I’ll just call thisbasic) is often seen as difficult with Qt. It really isn’t though.Qt Slots Thread Safe
There are three main ways I’ve seen people handle basic threading in their Qtapplications. The first is using system threads, either pthread or Windowsthreads. I don’t like this approach because you’re basically writing a portablethread library. You can use an already built portable thread library but whyuse a non-Qt solution when Qt already provides portable threading.
The two other approaches are defined by Qt’s QThreaddocumentation. 1) use a QObject worker. 2)subclass QThread and reimplement the run function.
I like using a QObject worker and I think this is the best (and easiest)approach. That said, I don’t like how Qt’s documentation explains this. It usestwo QObject classes to handle this. A variation of this approach is what I’mgoing to demonstrate.
The other documented approach is subclassing QThread. This isn’t a horribleidea but I don’t think it’s the cleanest approach. It also ties thefunctionality to a thread making code reuse low in this case. You could getaround this limitation by putting the functionality in a separate class but nowyou have two classes and the thread subclass really isn’t necessary when usingthe QObject worker approach.
This is a very simple example that demonstrates two types of workers. One takesarguments and runs until it’s task is finished. The second on runs until it’stold to stop. The second worker could take arguments if you need it to. Forthis example the workers both just increment a count every second and send theresult to a QMainWindow for it to be displayed.
Here is all the code that goes into the example which will have the main partsexplained after:
main.cpp
mainwindow.h
mainwindow.cpp
mainwindow.ui
countworker.h
countworker.cpp
infinitecountworker.h
infinitecountworker.cpp
portablesleep.h
threader.proCountWorker
This is a very simple object that increases a count every second. The start andend are set as part of the constructor. Setting up the object using theconstructor makes sense but it’s also because to start the worker we can’t passany arguments. This is a limitation of this method that the worker in Qt’s docsdoes not have. That said, I think it’s fine to use the constructor for argumentpassing. This method doesn’t reuse workers (not a limitation) so it’s fine.Once the worker finishes it’s work is all done and it stops.InfiniteCountWorker
This worker is very similar to the count worker. It’s to demonstrate infinitetasks unlike CountWorker which demonstrates finite tasks.
The key to his worker is the stopWork slot. Calling this sets sets a flag tolet the worker’s doWork function to exit.*Workers
Both workers have two signals in common, updateCount and finished.UpdateCount simply sends the current count off. The MainWindow connects anduses this to, well, update the count. finished signals that the worker is done.This is used to stop the thread and takes care of cleanup. The finished signalcan have multiple overloads. For example to send off a result in addition tosignaling that the worker is finished. You must have a no argument signal forcompletion with this method. You could use a different signal name instead ofoverloading “finished” if you want.
One thing to think about is thread synchronization. Well, with this workermethod you don’t need to worry about it. Initialization of parameters happensbefore the worker is moved to the thread and before the thread is even started.All passing (such as updateCount) happens using signals and slots. When passingdata between threads using signals and slots Qt handles thread synchronizationfor you. The stopWork function is called via a signal so the function runs onthe thread the work is running on between iterations of the while loop. Sothere is no need to wrap m_running in a mutex or other synchronizationtechniques.
It might be confusing that I said the stopWork function happens betweeniterations of the while loop in InfiniteCountWorker. This is because of theqApp->processEvents call. Without this, the signal to initiate the stopWorkslot won’t be delivered until after the while loop finishes. Which isimpossible in infiniteCountWorker. Remember the thread is a single thread andeverything running on it is single threaded. The while loop will block anythingelse in the object from running unless you use qApp->processEvents to allowsignals and slots to process. In the case of the CountWorkerqApp->processEvents isn’t really necessary since there aren’t any signalsthat are delivered to it. processEvents is only necessary for signals (fromoutside of or within the thread the worker is running) to initiate slots in theworker. It is not necessary for the worker to send off a signal (likeupdateCount) to another thread that it’s connected to.
If you haven’t realized by now that with the worker method (my variation orQt’s) the worker has an event loop. Subclassing QThread only does in somecases (see the documentation for when). You get signals into and out of workerwithout any additional code (except for a single processEvents call). You getthis essentially for free by using a worker!MainWindow
The workers aren’t really useful unless they can be started on a thread. That’swhat the MainWindow in this example is for. The startCount andstartInfiniteCount functions are the main thing to look at.startCount Breakdown
The thread and worker are created on the heap. The worker is initialized heretoo.
Put the worker on the thread so the worker will be run on the thread we createdinstead of the UI thread.
This is how the worker is started when the thread is started.
Connect all the finished signals. When the worker finishes the thread will bestopped with quit(), the worker will be deleted via deleteLater. Qt handlesdeletion and takes care of it when safe. This is another advantage of usingQObject workers. Also, when the worker finishes the countFinished functionis called so anything in the MainWindow that needs to happen when the workeris finished is run. Again, you could have a second finished signal (overload)that passes result data back to the MainWindow. Finally, when the threadfinishes (because of the worker’s finished signal calling the threads quitslot) it will be deleted by deleteLater. The deleteLater slots mean wedon’t need to track the the worker or thread pointers and worry about manuallydeleting them.
deleteLater is very useful. We have finished connected to multiple slots.Deleting a QObject when there are events pending or within a signal handler(slot) can lead to a crash. deleteLater ensures that this won’t happen bywaiting until all events are delivered. Also, you can’t delete a QObject froma thread so this also ensures the UI thread that created the worker and threadis where they are deleted. In this example this isn’t a concern but it’s niceto know that it won’t become one.
We don’t need to worry about something like dangling connections because when aQObject is deleted it automatically disconnects all signals and slots (not inall cases but that will be covered later).
Here the count will be updated on the MainWindow as it’s incremented in theworker. Again, there is no manual thread synchronization necessary because Qthandles this as part of it’s signal and slot system.
The last line to worry about actually starts the thread which in turns startsthe worker.startInfiniteCount Breakdown
This function is very similar to startCount. The only difference is one line.
The MainWindows button that will stop the InfiniteCountWorker is connectedto the InfiniteCountWorker’s stop function. Gregory charles casino niagara.PortableSleep
portablesleep.h is a header only cross platform sleep class. This is how wecreate the one second delay when counting. With Qt5 you can (should) useQThreadsmsleep or sleep public static functions instead. Qt4 on theother hand defines these functions as protected static functions. So there isno clean way to use them aside from creating a QThread subclass and exposingthem. Note that this example is not limited to Qt4. This example will runand work with both Qt4 and Qt5. A little bit of thought is all that’s needed toachieve this.
This information isn’t specific to threading but could be very useful to reallyget the most of the worker concept.
This example only uses int. This is a type that can be used with Qt’s signalsand slots as well as many other types such as QString. There are many complextypes like your own classes or even QMap<QString, QString>> cannot be usedimmediately with signals and slots.Qt Thread Slots Online
I say immediately because any class with a public constructor, copy constructorand destructor can be registered with Qt using qRegisterMetaType. This allowsthe object type to be used with signals and slots. For example you can use thefollowing in the MainWindow’s constructor to allow a complex type to be used.
The reason we need constructor, copy constructor and destructor is because whennecessary Qt will create a copy of an object and pass the copy to a slot. Inthe case of passing objects between threads using signals and slots a copy willbe passed to the slot. Remember primitive types like int and pointers arealways copies. Complex types may or may not be copies depending on thesituation.Realize that if you are passing a pointer between threads you will need manualsynchronization. If you’re allowing copies to be passed then you don’t need toworry about synchronization.
You’ve probably noticed that in this example the old style SIGNAL and SLOTmacros were used in the connect functions. This is on purpose because itsupports more compilers. Also, as mentioned, the example code works with bothQt4 (it is still used) and Qt5. I don’t really like ne

https://diarynote.indered.space

コメント

最新の日記 一覧

<<  2025年7月  >>
293012345
6789101112
13141516171819
20212223242526
272829303112

お気に入り日記の更新

テーマ別日記一覧

まだテーマがありません

この日記について

日記内を検索