c++ - Qt moveToThread 和数据竞争

标签 c++ multithreading qt

有一个类只有一个信号槽:

#ifndef SOME_CLASS_H
#define SOME_CLASS_H

#include <QObject>

class SomeClass : public QObject
{
    Q_OBJECT
public:
    explicit SomeClass(QObject *parent = 0) : QObject(parent) {
        connect(this, &SomeClass::valueChanged, this, &SomeClass::setValue);
    }

    void emitSignal() {
        emit valueChanged();
    }

signals:
    void valueChanged();
public slots:
    void setValue() {}
};

#endif // SOME_CLASS_H

这是定义单例的程序的主文件,它创建上面定义的类的实例并将其放入单独的线程中:

#include <QCoreApplication>
#include <QThread>
#include <someclass.h>

class Singleton {
public:
    QThread t;
    SomeClass someClassObject;
    Singleton() {
        someClassObject.moveToThread(&t);
        t.start();
    }

    static SomeClass& getInstance() {
        static Singleton st;
        return st.someClassObject;
    }

    ~Singleton() {
        t.quit();
        t.wait();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    SomeClass& b = Singleton::getInstance();
    b.emitSignal();

    QThread::msleep(100);

    return a.exec();
}

问题是,当我使用一些线程检查工具(如 ThreadSanitizer 或 Helgrind)评估此程序时,会报告几个关于数据竞争条件的警告。 ThreadSanitizer 的示例输出:

==================
WARNING: ThreadSanitizer: data race (pid=3827)
  Read of size 8 at 0x7d040000f6a0 by thread T1:
    #0 void QtPrivate::FunctionPointer<void (SomeClass::*)()>::call<void, void>(void (SomeClass::*)(), SomeClass*, void**) /usr/include/qt5/QtCore/qobjectdefs_impl.h:142 (exe+0x0000000a2d1c)
    #1 QtPrivate::QSlotObject<void (SomeClass::*)(), void, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) /usr/include/qt5/QtCore/qobject_impl.h:149 (exe+0x0000000a29b4)
    #2 QObject::event(QEvent*) ??:0 (libQt5Core.so.5+0x00000029022d)

  Previous write of size 8 at 0x7d040000f6a0 by main thread:
    #0 malloc ??:0 (exe+0x0000000426e9)
    #1 QMetaObject::activate(QObject*, int, int, void**) ??:0 (libQt5Core.so.5+0x00000028ef1c)
    #2 main /tmp/project/main.cpp:31 (exe+0x0000000a19f2)

  Location is heap block of size 8 at 0x7d040000f6a0 allocated by main thread:
    #0 malloc ??:0 (exe+0x0000000426e9)
    #1 QMetaObject::activate(QObject*, int, int, void**) ??:0 (libQt5Core.so.5+0x00000028ef1c)
    #2 main /tmp/project/main.cpp:31 (exe+0x0000000a19f2)

  Thread T1 'QThread' (tid=3829, running) created by main thread at:
    #0 pthread_create ??:0 (exe+0x000000046f6b)
    #1 QThread::start(QThread::Priority) ??:0 (libQt5Core.so.5+0x0000000928b7)
    #2 Singleton::getInstance() /tmp/project/main.cpp:16 (exe+0x0000000a1b3c)
    #3 main /tmp/project/main.cpp:30 (exe+0x0000000a19bf)

SUMMARY: ThreadSanitizer: data race /usr/include/qt5/QtCore/qobjectdefs_impl.h:142 void QtPrivate::FunctionPointer<void (SomeClass::*)()>::call<void, void>(void (SomeClass::*)(), SomeClass*, void**)
==================
==================
WARNING: ThreadSanitizer: data race (pid=3827)
  Write of size 1 at 0x7d040000f6b0 by thread T1:
    #0 free ??:0 (exe+0x000000042dfb)
    #1 QMetaCallEvent::~QMetaCallEvent() ??:0 (libQt5Core.so.5+0x00000028d3a4)

  Previous write of size 8 at 0x7d040000f6b0 by main thread:
    #0 malloc ??:0 (exe+0x0000000426e9)
    #1 QMetaObject::activate(QObject*, int, int, void**) ??:0 (libQt5Core.so.5+0x00000028eefe)
    #2 main /tmp/project/main.cpp:31 (exe+0x0000000a19f2)

  Location is heap block of size 4 at 0x7d040000f6b0 allocated by main thread:
    #0 malloc ??:0 (exe+0x0000000426e9)
    #1 QMetaObject::activate(QObject*, int, int, void**) ??:0 (libQt5Core.so.5+0x00000028eefe)
    #2 main /tmp/project/main.cpp:31 (exe+0x0000000a19f2)

  Thread T1 'QThread' (tid=3829, running) created by main thread at:
    #0 pthread_create ??:0 (exe+0x000000046f6b)
    #1 QThread::start(QThread::Priority) ??:0 (libQt5Core.so.5+0x0000000928b7)
    #2 Singleton::getInstance() /tmp/project/main.cpp:16 (exe+0x0000000a1b3c)
    #3 main /tmp/project/main.cpp:30 (exe+0x0000000a19bf)

SUMMARY: ThreadSanitizer: data race ??:0 free
==================
==================
WARNING: ThreadSanitizer: data race (pid=3827)
  Write of size 8 at 0x7d180000edc0 by thread T1:
    #0 operator delete(void*) ??:0 (exe+0x00000004392b)
    #1 QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ??:0 (libQt5Core.so.5+0x000000269e0f)

  Previous write of size 8 at 0x7d180000edc0 by main thread:
    #0 operator new(unsigned long) ??:0 (exe+0x0000000432b9)
    #1 QMetaObject::activate(QObject*, int, int, void**) ??:0 (libQt5Core.so.5+0x00000028ef94)
    #2 main /tmp/project/main.cpp:31 (exe+0x0000000a19f2)

  Location is heap block of size 88 at 0x7d180000edc0 allocated by main thread:
    #0 operator new(unsigned long) ??:0 (exe+0x0000000432b9)
    #1 QMetaObject::activate(QObject*, int, int, void**) ??:0 (libQt5Core.so.5+0x00000028ef94)
    #2 main /tmp/project/main.cpp:31 (exe+0x0000000a19f2)

  Thread T1 'QThread' (tid=3829, running) created by main thread at:
    #0 pthread_create ??:0 (exe+0x000000046f6b)
    #1 QThread::start(QThread::Priority) ??:0 (libQt5Core.so.5+0x0000000928b7)
    #2 Singleton::getInstance() /tmp/project/main.cpp:16 (exe+0x0000000a1b3c)
    #3 main /tmp/project/main.cpp:30 (exe+0x0000000a19bf)

SUMMARY: ThreadSanitizer: data race ??:0 operator delete(void*)
==================

是什么导致了这些竞争条件?

最佳答案

由于警告都源自与信号、槽和事件循环相关的 Qt 内部,我认为检测到的竞争条件是误报。 Helgrind 是否检测到相同的竞争条件?一些人 ( http://www.kdab.com/~dfaure/helgrind.html ) 报告了 Qt 的 Helgrind 错误,并且最近似乎已得到解决。

关于c++ - Qt moveToThread 和数据竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25460994/

相关文章:

c++ - 我的 Qt5 程序如何从控制台获取参数?

c++ - 如何在 Qt 5 中写入和读取 QResource 文件?

c++ - SDL 在屏幕不起作用之前制作图像

Java - 线程在没有任何指示的情况下停止

c - 嵌套线程创建期间的数据竞争

python - 为什么QUndoStack.push()会执行QUndoCommand.redo()?

c++ - Rapidxml:直接添加子树作为值

c++ - Win32 C/C++ 从内存缓冲区加载图像

c++ - 在 Cython 中做列表/字典的惯用方式?

java - 如何在保持线程安全的同时降低锁定粒度?