Re[3]: COW and thread safety in Qt
От: andyp  
Дата: 11.10.23 15:23
Оценка:
Здравствуйте, Skorodum, Вы писали:

S>Это на коде адаптированном под версия <5.10 который вы привели выше?

S>Можете показать отладачную печать?

Код, которым тестировалось (константная ссылка). Для теста по значению поменять определения сигнала-слота в Bar.

  main.cpp
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QScopedPointer>
#include <QtCore/QThread>
#include <QtCore/QTimer>

#define PRINT_FUNCTION_INFO qDebug() << Q_FUNC_INFO << this << QThread::currentThread();

struct Foo {
    Foo()                       { PRINT_FUNCTION_INFO }
    Foo(const Foo&)             { PRINT_FUNCTION_INFO }
    Foo(Foo&&)                  { PRINT_FUNCTION_INFO }
    Foo& operator=(const Foo&)  { PRINT_FUNCTION_INFO return *this; }
    Foo& operator=(Foo&&)       { PRINT_FUNCTION_INFO return *this; }
    ~Foo()                      { PRINT_FUNCTION_INFO }
};

class Bar : public QObject {
    Q_OBJECT

public:
    explicit Bar(QObject* parent = nullptr) : QObject(parent) {}
    void emitSignals() { emit dataByValueSignal(foos); }
    QVector<Foo> foos;

signals:
    void dataByValueSignal(const QVector<Foo>&) const;

public slots:
    void dataByValue(const QVector<Foo>& foo)
    {
        qDebug() << QThread::currentThread() << Q_FUNC_INFO << foo.size();
        // It is ok to pass "big" QVector object by value because of COW (but const reference is still better).
        // Also can safely modify copy of 'COW class' transfered with signals and slots in another thread.
        // Using non-const methods will make deep copy of QVector content behind the scene.
        //foo.append(Foo()); // <- comment out to see effect of COW
        foos = foo;
        qDebug() << QThread::currentThread() << "consumer data" << QString::number( uint64_t(foos.constData()), 16 );
    }
};

class Producer : public QObject
{
    Q_OBJECT
    const Bar& bar;
public:
    Producer(const Bar& b): bar(b){}
public slots:
    void do_produce() {
        Bar anotherBar;
        anotherBar.foos.append(Foo());

        // connect to object in another thread and pass object with COW by value
        // no copies of QVector *content* at this point and thread safe
        qDebug() << QThread::currentThread() << "producer data before signal" << QString::number( uint64_t(anotherBar.foos.constData()), 16 );
        QObject::connect(&anotherBar, &Bar::dataByValueSignal, &bar, &Bar::dataByValue);
        anotherBar.emitSignals();
        qDebug() << QThread::currentThread() << "producer data after signal" << QString::number( uint64_t(anotherBar.foos.constData()), 16 );
        // can safely modify local copy of Qt class
        QThread::sleep(1);
        anotherBar.foos.pop_front();
        qDebug() << QThread::currentThread() << anotherBar.foos.size();
    }
};

int main(int argc, char *argv[])
{
    // main thread
    const Bar bar;

    qRegisterMetaType<QVector<Foo>>("QVector<Foo>");
    const QScopedPointer<QThread> thread(new QThread);

    Producer prod(bar);
    prod.moveToThread(thread.data());

    const QCoreApplication a(argc, argv);
    QObject::connect(thread.data(), &QThread::started, &prod, &Producer::do_produce);
    QObject::connect(thread.data(), &QThread::finished, qApp, &QCoreApplication::quit);
    QTimer::singleShot(0, qApp, [&thread]{thread->start();}); // start thread when event loop is running
    return qApp->exec();
}

#include "main.moc"


Выхлоп release, по значению

Foo::Foo() 0x7f7633163c98 QThread(0x563e12505fe0)
Foo::Foo(Foo&&) 0x7f762c005c58 QThread(0x563e12505fe0)
Foo::~Foo() 0x7f7633163c98 QThread(0x563e12505fe0)
QThread(0x563e12505fe0) producer data before signal "7f762c005c58"
QThread(0x563e12505fe0) producer data after signal "7f762c005c58"
QThread(0x563e12505e20) void Bar::dataByValue(QVector<Foo>) 1
QThread(0x563e12505e20) consumer data "7f762c005c58"
Foo::Foo(const Foo&) 0x7f762c0054d8 QThread(0x563e12505fe0)
Foo::~Foo() 0x7f762c0054d8 QThread(0x563e12505fe0)
QThread(0x563e12505fe0) 0

Выхлоп release, по константной ссылке
Foo::Foo() 0x7f4b1df57c98 QThread(0x5610c684dfe0)
Foo::Foo(Foo&&) 0x7f4b18005c58 QThread(0x5610c684dfe0)
Foo::~Foo() 0x7f4b1df57c98 QThread(0x5610c684dfe0)
QThread(0x5610c684dfe0) producer data before signal "7f4b18005c58"
QThread(0x5610c684dfe0) producer data after signal "7f4b18005c58"
QThread(0x5610c684de20) void Bar::dataByValue(const QVector<Foo>&) 1
QThread(0x5610c684de20) consumer data "7f4b18005c58"
Foo::Foo(const Foo&) 0x7f4b180054d8 QThread(0x5610c684dfe0)
Foo::~Foo() 0x7f4b180054d8 QThread(0x5610c684dfe0)
QThread(0x5610c684dfe0) 0
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.