My Qt4 code uses some QThread instances which operate on a common data structure which contains some QString fields. It can be boiled down to the following:
My data structure:
class My : public QObject{
Q_OBJECT
public:
QString foo;
};
The thread implementation:
class Thr : public QThread{
public:
My* my;
protected:
void run(){
while (true){
QString copy = my->foo;
QString bar = copy.toUpper();
my->foo = bar.toLower();
}
}
};
This is a test application written for my research on the problem. Of course, it does not do anything actually useful :)
If I initialize one instance of My
and start one thread with that instance, everything is fine. But when I start a second one with the same My
instance, it crashes with different messages looking like some heap/stack/whatever corruption.
Is that normal? I am aware about multithreading issues in general and also about Qt's QMutex, which can avoid the problem. But as far as I understood the Qt documentation correctly, I am allowed to use it this way. I do not operate on the same instance of QString simultaneously (possibly I do due to some fancy implicit sharing mechanism - but the documentation states that this is fully transparent for the user?!).
As mentioned, my question is not about how to rewrite the code, but what piece of "Beginning with Qt 4, implicit shared classes can safely be copied across threads, like any other value classes. They are fully reentrant. The implicit sharing is really implicit." (http://qt-project.org/doc/qt-4.8/threads-modules.html) I have misunderstood.
As noted in the comment, you are probably trying to write the same data from different threads. I am writing "probably" only because you have not shared the usage of your QThread
subclass.
As pointed out in the comment, not even an assignment is guaranteed by the C++ standard to be thread-safe for classes like your Object
subclass with a QString
inside.
You could use std::atomic
as per hint, although it has only been available since C++11. A more cross-solution would be to use QMutex
for your scenario, or probably evn better to use the RAII solution with QMutexLocker
which will take care of the unlocking for you, automatically. So you would be something like the code below.
class Thr : public QThread{
public:
My* my;
QMutex mutex; // <--- first addition
protected:
void run(){
while (true){
QString copy = my->foo;
QString bar = copy.toUpper();
QMutexLocker locker(&mutex); // <--- second addition
my->foo = bar.toLower();
}
}
};
Of course, the recommended solution depends on further factors, for instance, you could also redesign your program not to use a pointer depending on the exact use case at hand, et al.