Qt4 C++:来自多个线程的QString变量使用崩溃

Qt4 C++: QString variable usage from multiple threads crashes

本文关键字:QString 变量 崩溃 线程 C++ Qt4      更新时间:2023-10-16

我的Qt4代码使用一些QThread实例,这些实例在包含一些QString字段的公共数据结构上操作。它可以归结为以下内容:

我的数据结构:

class My : public QObject{
Q_OBJECT
public:
QString foo;
};

线程实现:

class Thr : public QThread{
public:
My* my;
protected:
void run(){
while (true){
QString copy = my->foo;
QString bar = copy.toUpper();
my->foo = bar.toLower();
}
}
};

这是我为研究这个问题而写的测试应用程序。当然,它并没有做任何实际有用的事情:)

如果我初始化My的一个实例,并用该实例启动一个线程,那么一切都很好。但是,当我用相同的My实例启动第二个实例时,它会崩溃,出现不同的消息,看起来像是堆/堆栈/任何损坏。

这正常吗?我知道一般的多线程问题,也知道Qt的QMutex,它可以避免这个问题。但就我正确理解Qt文档而言,我可以这样使用它。我不会同时对同一个QString实例进行操作(可能是因为一些奇特的隐式共享机制,但文档中指出这对用户来说是完全透明的?!)。

如前所述,我的问题不是如何重写代码,而是"从Qt4开始,隐式共享类可以像任何其他值类一样安全地跨线程复制。它们是完全可重入的。隐式共享真的是隐式的。"(http://qt-project.org/doc/qt-4.8/threads-modules.html)我误解了。

如注释中所述,您可能正试图从不同的线程写入相同的数据。我写"可能"只是因为你没有分享你的QThread子类的用法。

正如注释中所指出的,对于像Object子类这样内部有QString的类,C++标准甚至不能保证赋值是线程安全的。

您可以根据提示使用std::atomic,尽管它从C++11开始才可用。一个更交叉的解决方案是在您的场景中使用QMutex,或者可能更好地将RAII解决方案与QMutexLocker一起使用,后者将自动为您解锁。所以你会像下面的代码一样。

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();
}
}
};

当然,推荐的解决方案取决于其他因素,例如,你也可以根据手头的确切用例重新设计你的程序,不使用指针,等等