【问题标题】:Qt4 C++: QString variable usage from multiple threads crashesQt4 C++:来自多线程崩溃的 QString 变量使用
【发布时间】:2013-12-22 23:42:10
【问题描述】:

我的 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 的同一个实例进行操作(可能是因为一些花哨的隐式共享机制 - 但文档指出这对用户来说是完全透明的?!)。

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

【问题讨论】:

  • 同时访问同一个对象,如果是修改,至少有一个,创建一个数据竞争。具有数据竞争的程序具有未定义的行为。在我看来,您的几个线程正在同时修改(通过分配)my->foo (my->foo = bar.toLower();)。
  • 好的,那么,在分配中,foo 实例可能处于不一致的“中间”状态?我确实希望,至少那些非常基本的 Qt 类型的赋值本身是一个线程安全的操作,所以我可以同时访问它,我得到旧值或新值,但不是崩溃:) 所以我已经使用指针或互斥锁,这并没有真正使代码更具可读性......
  • C++ 也不保证指针分配是原子的;) 任何你不包含在 std::atomic<foo> 中的东西都需要受到保护以防止数据竞争。
  • Aargh :) 非常感谢您的解释、警告和 std::atomic 提示!

标签: c++ qt qthread qstring qtcore


【解决方案1】:

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

正如评论中所指出的,对于像 Object 子类内部带有 QString 的类,C++ 标准甚至不能保证分配是线程安全的。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-15
    • 1970-01-01
    相关资源
    最近更新 更多