A quick multithreading question
-
I am using multithreading in my QT program. I need to pass data to the worker object that lives in the worker thread. I created a setData function in a QObject subclass to pass all the necessary data from the main gui thread. However I verified the function is called from the main thread by looking at QThread::currentThreadId() in the setData function. This won't have a negative impact on ensuring the worker thread gets its own copy of the data to be in line with reentrancy requirements will it? Also are basic data types like int/bool/qint64 reentrant?
Thanks for the help -
Hi,
Multithreaded questions are never quick ;)
Have a look here about the principle of reentrancy. It's about how your functions are designed not about the datatype themselves even though you also have to handle them carefully.
-
@SGaist said in A quick multithreading question:
Have a look here about the principle of reentrancy.
Which article (even) I hardly understand. ;)
The rule of thumb is:Reentrancy:
- if you don't use static/global variables, your function is reentrant.
- if you have global/statics, it is not reentrant.
Thread-safety:
- If you made sure all shared resources are only manipulated by a single thread at any one time, either by using a synchronization primitive or atomic operations, then your function is thread-safe.
- If you have no guarantee that shared resources are protected, then your function is not thread-safe
From the above 4 you can have all combinations, e.g. a function can be reentrant, but not thread-safe, which incidentally is your case. And it is not thread safe because your function can be called from different threads and can modify the internals of the class from different threads without you taking measures to prevent race conditions.
What you can do to solve this is:
- make your function thread-safe - meaning putting the necessary locks (a mutex should be enough)
- or much better - call the function in the other thread's context, by means of
QMetaObject::invokeMethod
or a signal-slot connection.
Now you see why @SGaist remarked that threading questions never turn out to be "quick". ;)
Kind regards.
-
Thanks guys. Is it ok though to call a worker object function (the object has been moved to the worker thread) from the main thread to pass the data to the worker object? Does this ensure that the worker thread has its own copy of the data as is required for a reentrant class even though the worker thread function is being called from the main thread? Keep in mind this is happening before the worker thread is started.
And about the basic data types, if they are used in a class without dynamic memory and no static global variables is that class reentrant as long as all of its other data is reentrant? (it's got reentrant data members like qstrings, qlists etc in addition the the basic ints bools etc)
-
@Crag_Hack said in A quick multithreading question:
Is it ok though to call a worker object function (the object has been moved to the worker thread) from the main thread to pass the data to the worker object?
I mentioned this. If your function isn't thread-safe, then no, it is not. (but also see the comment 2 lines below)
Does this ensure that the worker thread has its own copy of the data as is required for a reentrant class even though the worker thread function is being called from the main thread?
This depends on your data organization entirely. If you don't have static variables, then yes, your class will be reentrant. But you still have to make sure it's thread-safe if you intend to call methods from different threads.
Keep in mind this is happening before the worker thread is started.
Then it's safe, but then you call all functions in one thread and you don't have any race conditions to take care of.
And about the basic data types, if they are used in a class without dynamic memory and no static global variables is that class reentrant as long as all of its other data is reentrant?
Yes. Primitive types are reentrant, and if all of your datatypes that you use in the class (i.e. other classes) are reentrant, then the class is reentrant as well.
it's got reentrant data members like qstrings, qlists etc in addition the the basic ints bools etc
This is all fine.
-
I'd prefer to be on the safe side for this since this is a data backup program. For signal/slot I just use a signal slot between the main thread and worker thread or invokemethod with a slot in the worker thread? The function then gets called in the worker thread which is a safe way of passing the data correct? Also the data to be passed is a custom class I wrote so I'd have to register it with the signal slot mechanism right? And do I need an event loop requiring use of exec() on the thread to support signals/slots?
Also is I were to use mutex locks would I only need to lock the one function called from the main thread or all functions using the data to be passed? And a conceptual question I've been wondering about... if you share data between the main thread and worker thread do you need to use mutexes on the main thread at all?
Also considering the worker thread isn't started until the data's passed perhaps I could just avoid both of these approaches considering you mentioned such is safe to do?
Thanks for the help I think I'm about ready to proceed and tackle this sucker.
-
@Crag_Hack said in A quick multithreading question:
For signal/slot I just use a signal slot between the main thread and worker thread or invokemethod with a slot in the worker thread? The function then gets called in the worker thread which is a safe way of passing the data correct?
Yes, the signal-slot mechanism or/and
QMetaObject::invokeMethod
are safe to use across threads. If you use these and the data you pass around is reentrant, then you needn't worry about thread safety. These two ways guarantee that the method is called in the context of theQObject
's thread (QObject
s have thread affinity)Also the data to be passed is a custom class I wrote so I'd have to register it with the signal slot mechanism right?
Yes, correct. To be able to pass the instances of your class with any of the two aforementioned methods between threads you have to declare it as a metatype and register it at runtime.
Also is I were to use mutex locks would I only need to lock the one function called from the main thread or all functions using the data to be passed? And a conceptual question I've been wondering about... if you share data between the main thread and worker thread do you need to use mutexes on the main thread at all?
A mutex (Mutual exclusion) is a synchronization primitive which is used to protect a shared resource from concurrent writes (when all operations are non-modifying there's no need to guard the data). As such it should be accessible to all threads that want to access the piece of data it is protecting - it basically means that the threads are sharing not only the data but also the synchronization primitive. I think this should answer the second question - not one for each thread, you need one mutex that's accessible from all threads that share the data. One per shared data block.
The first question is wrong. What you need to worry is not the data you pass to the function, but the data the function modifies - that is your class' members. For example:
void MyClass::functionICallFromDifferentThreads(QString reentrantData) { // Do stuff with reentrantData, no other thread cares, because it's reentrant // Be careful when you write to the class members, though. Multiple threads can't write at the same time. // To avoid a race condition, we need to introduce a guarding mechanism (a mutex) for the following line. this->myClassMember = reentrantData; //< Ooops! myClassMember is now shared between threads, we need to guard it. }
Also considering the worker thread isn't started until the data's passed perhaps I could just avoid both of these approaches considering you mentioned such is safe to do?
Until you actually call
QThread::start()
there's no concurrency (the worker thread isn't running), thus you have none of the above problems.I hope that clears it.
Kind regards.