QThread
hoạt động bên trong của chúng không liên quan: chúng không đóng vai trò gì trong cách vòng lặp sự kiện hoạt động. Khi bạn emit
tín hiệu trong một QObject
sống trong một chủ đề khác với đối tượng của vị trí, tín hiệu sẽ được đăng dưới dạng QMetaCallEvent
vào hàng đợi sự kiện của chuỗi nhận. Vòng lặp sự kiện đang chạy trong chuỗi nhận sẽ sau đó hành động trên sự kiện này và thực hiện cuộc gọi vào khe được kết nối với tín hiệu phát ra. Vì vậy, không có vấn đề gì xảy ra, bất cứ dữ liệu nào bạn gửi qua tín hiệu cuối cùng sẽ kết thúc dưới dạng tải trọng trong một thể hiện của lớp dẫn xuất từ QEvent.
Thịt của vấn đề là khi QMetaCallEvent
đến vòng lặp sự kiện và vùng chứa được chuyển vào vị trí làm đối số. Tất nhiên các nhà xây dựng bản sao có thể được gọi là nhiều lần trên đường đi.Dưới đây là một số mã đơn giản mà thể hiện bao nhiêu lần so với constructor sao chép và mặc định constructor được trong thực tế gọi là
trên các yếu tố của các thành viên dữ liệu của một container ngầm chia sẻ copy-on-write (QVector),
trên lớp tùy chỉnh phù hợp cho vùng chứa.
Bạn sẽ được ngạc nhiên :)
Kể từ container Qt đang ngầm chia sẻ copy-on-write, xây dựng bản sao của họ có chi phí không đáng kể: tất cả đã xong là một bộ đếm tham chiếu được tăng lên nguyên tử xây dựng . Chẳng hạn, không có thành viên dữ liệu nào được sao chép.
Than ôi, trước 11 C++ cho thấy mặt xấu của nó: nếu mã vùng sửa đổi vùng chứa theo bất kỳ cách nào, không có cách nào chuyển tham chiếu đến vị trí theo cách sao cho trình biên dịch biết rằng vùng chứa ban đầu là không cần nữa. Do đó: nếu khe nhận được tham chiếu const đến vùng chứa, bạn được đảm bảo rằng sẽ không có bản sao nào được tạo. Nếu vị trí nhận được bản sao có thể ghi của vùng chứa và bạn sửa đổi nó, sẽ có bản sao hoàn toàn không cần thiết được thực hiện vì phiên bản còn lại tại trang cuộc gọi không còn cần thiết nữa. Trong C++ - 11 bạn sẽ chuyển một tham chiếu rvalue làm tham số. Việc chuyển một tham chiếu rvalue trong một cuộc gọi hàm kết thúc vòng đời của đối tượng được truyền trong người gọi. đầu ra mã
mẫu:
"Started" copies: 0 assignments: 0 default instances: 0
"Created Foo" copies: 0 assignments: 0 default instances: 100
"Created Bar" copies: 0 assignments: 0 default instances: 100
"Received signal w/const container" copies: 0 assignments: 0 default instances: 100
"Received signal w/copy of the container" copies: 0 assignments: 0 default instances: 100
"Made a copy" copies: 100 assignments: 1 default instances: 101
"Reset" copies: 0 assignments: 0 default instances: 0
"Received signal w/const class" copies: 2 assignments: 0 default instances: 1
"Received signal w/copy of the class" copies: 3 assignments: 0 default instances: 1
//main.cpp
#include <QtCore>
class Class {
static QAtomicInt m_copies;
static QAtomicInt m_assignments;
static QAtomicInt m_instances;
public:
Class() { m_instances.fetchAndAddOrdered(1); }
Class(const Class &) { m_copies.fetchAndAddOrdered(1); }
Class & operator=(const Class &) { m_assignments.fetchAndAddOrdered(1); return *this; }
static void dump(const QString & s = QString()) {
qDebug() << s << "copies:" << m_copies << "assignments:" << m_assignments << "default instances:" << m_instances;
}
static void reset() {
m_copies = 0;
m_assignments = 0;
m_instances = 0;
}
};
QAtomicInt Class::m_instances;
QAtomicInt Class::m_copies;
QAtomicInt Class::m_assignments;
typedef QVector<Class> Vector;
Q_DECLARE_METATYPE(Vector)
class Foo : public QObject
{
Q_OBJECT
Vector v;
public:
Foo() : v(100) {}
signals:
void containerSignal(const Vector &);
void classSignal(const Class &);
public slots:
void sendContainer() { emit containerSignal(v); }
void sendClass() { emit classSignal(Class()); }
};
class Bar : public QObject
{
Q_OBJECT
public:
Bar() {}
signals:
void containerDone();
void classDone();
public slots:
void containerSlotConst(const Vector &) {
Class::dump("Received signal w/const container");
}
void containerSlot(Vector v) {
Class::dump("Received signal w/copy of the container");
v[99] = Class();
Class::dump("Made a copy");
Class::reset();
Class::dump("Reset");
emit containerDone();
}
void classSlotConst(const Class &) {
Class::dump("Received signal w/const class");
}
void classSlot(Class) {
Class::dump("Received signal w/copy of the class");
emit classDone();
//QThread::currentThread()->quit();
}
};
int main(int argc, char ** argv)
{
QCoreApplication a(argc, argv);
qRegisterMetaType<Vector>("Vector");
qRegisterMetaType<Class>("Class");
Class::dump("Started");
QThread thread;
Foo foo;
Bar bar;
Class::dump("Created Foo");
bar.moveToThread(&thread);
Class::dump("Created Bar");
QObject::connect(&thread, SIGNAL(started()), &foo, SLOT(sendContainer()));
QObject::connect(&foo, SIGNAL(containerSignal(Vector)), &bar, SLOT(containerSlotConst(Vector)));
QObject::connect(&foo, SIGNAL(containerSignal(Vector)), &bar, SLOT(containerSlot(Vector)));
QObject::connect(&bar, SIGNAL(containerDone()), &foo, SLOT(sendClass()));
QObject::connect(&foo, SIGNAL(classSignal(Class)), &bar, SLOT(classSlotConst(Class)));
QObject::connect(&foo, SIGNAL(classSignal(Class)), &bar, SLOT(classSlot(Class)));
QObject::connect(&bar, SIGNAL(classDone()), &thread, SLOT(quit()));
QObject::connect(&thread, SIGNAL(finished()), &a, SLOT(quit()));
thread.start();
a.exec();
thread.wait();
}
#include "main.moc"
Chúng ta cần phải tìm hiểu thêm. Có thể chấp nhận chủ đề chính để mất dữ liệu không? Chủ đề chính làm gì với dữ liệu? Nhưng bất kể nhu cầu của bạn, tôi không thể tin rằng phát ra một mảng là giải pháp tối ưu. – TonyK