Trước hết, bạn nên có lẽ không đặt bất kỳ khe trong QThreads. QThreads không thực sự có nghĩa là có nguồn gốc từ khác hơn là reimplementing phương thức run
và phương thức riêng (không phải tín hiệu!).
QThread là một bộ điều khiển chuỗi, không phải là chính một chuỗi. Trong hầu hết trường hợp, bạn nên đối phó với QObjects. Bắt đầu một chuỗi, sau đó di chuyển đối tượng đối tượng đến chủ đề đó. Đó là cách duy nhất bạn sẽ nhận được các khe hoạt động chính xác trong chuỗi. Di chuyển thể hiện luồng (nó là Bắt nguồn từ QObject!) Cho chuỗi là kiểu hack và kiểu xấu. Đừng làm điều đó bất chấp các bài đăng trên diễn đàn không được thông báo nói cách khác.
Đối với phần còn lại của câu hỏi: cuộc gọi khe tín hiệu không phải xác định bất kỳ điều gì cũng như xác thực nhiều. "Vị trí" và "xác nhận" được thực hiện khi kết nối được thiết lập. Các bước chính được thực hiện tại thời điểm gọi là:
Khóa đột biến vị trí tín hiệu từ một hồ bơi.
Lặp lại qua danh sách kết nối.
Thực hiện cuộc gọi bằng cách sử dụng cuộc gọi trực tiếp hoặc hàng đợi.
Chi phí chung
Bất kỳ cuộc gọi tín hiệu-khe luôn bắt đầu như một cuộc gọi trực tiếp trong việc thực hiện của tín hiệu được tạo ra bởi moc. Một mảng các đối số của tín hiệu được xây dựng trên ngăn xếp. Các đối số không được sao chép.
Tín hiệu sau đó gọi QMetaObject::activate
, trong đó danh sách kết nối mutex được mua và danh sách các vị trí được kết nối được lặp lại, đặt cuộc gọi cho từng vị trí.
Connections Direct
Không có nhiều được thực hiện ở đó, các khe cắm được gọi bằng cách gọi trực tiếp QObject::qt_static_metacall
thu được tại thời điểm kết nối được thành lập, hoặc QObject::qt_metacall
nếu QMetaObject::connect
được sử dụng để thiết lập kết nối. Cái sau cho phép dynamic creation of signals and slots.
Connections xếp hàng đợi
Những lập luận phải marshalled và sao chép, kể từ khi cuộc gọi phải được lưu trữ trong một hàng đợi sự kiện và các tín hiệu phải trả lại. Điều này được thực hiện bằng cách phân bổ một mảng các con trỏ tới các bản sao, và sao chép-consting mỗi đối số trên heap. Mã để làm điều đó thực sự không đơn giản là cũ C.
Việc xếp hàng cuộc gọi được thực hiện trong vòng queued_activate
. Đây là nơi mà việc xây dựng bản sao được thực hiện.
Chi phí của cuộc gọi xếp hàng luôn là ít nhất một phân bổ heap là QMetaCallEvent
. Nếu cuộc gọi có bất kỳ đối số nào, thì một mảng con trỏ tới đối số được cấp phát và một phân bổ bổ sung được thực hiện cho mỗi đối số. Đối với cuộc gọi với các đối số n
, chi phí được cung cấp dưới dạng biểu thức C là phân bổ (n ? 2+n : 1)
. Giá trị trả về cho các cuộc gọi chặn là truy cập dưới dạng đối số. Có thể cho rằng, khía cạnh này của Qt có thể được tối ưu hóa xuống một phân bổ cho tất cả mọi thứ, nhưng trong cuộc sống thực nó chỉ là vấn đề nếu bạn đang gọi các phương pháp tầm thường.
Benchmark Kết quả
Ngay cả một trực tiếp (không xếp hàng đợi) gọi tín hiệu-khe có overhead đo lường được, nhưng bạn phải chọn trận đánh của bạn. Dễ kiến trúc mã so với hiệu suất. Bạn đo lường hiệu suất của ứng dụng cuối cùng của bạn và xác định tắc nghẽn, phải không? Nếu bạn làm như vậy, bạn có thể thấy rằng trong các ứng dụng thực tế, chi phí đầu vào khe tín hiệu không có vai trò gì.
Cơ chế khe thời gian duy nhất có chi phí đáng kể là nếu bạn đang gọi các chức năng tầm thường. Giả sử, nếu bạn gọi đến số trivial
trong mã bên dưới. Đó là một tiêu chuẩn hoàn chỉnh, độc lập, vì vậy hãy tự do chạy nó và tự mình xem. Các kết quả trên máy tính của tôi là:
Warming up the caches...
trivial direct call took 3ms
nonTrivial direct call took 376ms
trivial direct signal-slot call took 158ms, 5166% longer than direct call.
nonTrivial direct signal-slot call took 548ms, 45% longer than direct call.
trivial queued signal-slot call took 2474ms, 1465% longer than direct signal-slot and 82366% longer than direct call.
nonTrivial queued signal-slot call took 2474ms, 416% longer than direct signal-slot and 653% longer than direct call.
gì cần lưu ý, có lẽ, là concatenating dây khá nhanh :)
Lưu ý rằng tôi đang làm các cuộc gọi thông qua một con trỏ hàm, điều này là để ngăn chặn trình biên dịch tối ưu hóa các cuộc gọi trực tiếp đến chức năng bổ sung.
//main.cpp
#include <cstdio>
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QElapsedTimer>
#include <QTextStream>
static const int n = 1000000;
class Test : public QObject
{
Q_OBJECT
public slots:
void trivial(int*, int, int);
void nonTrivial(QString*, const QString&, const QString&);
signals:
void trivialSignalD(int*, int, int);
void nonTrivialSignalD(QString*, const QString&, const QString &);
void trivialSignalQ(int*, int, int);
void nonTrivialSignalQ(QString*, const QString&, const QString &);
private slots:
void run();
private:
void benchmark(bool timed);
void testTrivial(void (Test::*)(int*,int,int));
void testNonTrivial(void (Test::*)(QString*,const QString&, const QString&));
public:
Test();
};
Test::Test()
{
connect(this, SIGNAL(trivialSignalD(int*,int,int)),
SLOT(trivial(int*,int,int)), Qt::DirectConnection);
connect(this, SIGNAL(nonTrivialSignalD(QString*,QString,QString)),
SLOT(nonTrivial(QString*,QString,QString)), Qt::DirectConnection);
connect(this, SIGNAL(trivialSignalQ(int*,int,int)),
SLOT(trivial(int*,int,int)), Qt::QueuedConnection);
connect(this, SIGNAL(nonTrivialSignalQ(QString*,QString,QString)),
SLOT(nonTrivial(QString*,QString,QString)), Qt::QueuedConnection);
QTimer::singleShot(100, this, SLOT(run()));
}
void Test::run()
{
// warm up the caches
benchmark(false);
// do the benchmark
benchmark(true);
}
void Test::trivial(int * c, int a, int b)
{
*c = a + b;
}
void Test::nonTrivial(QString * c, const QString & a, const QString & b)
{
*c = a + b;
}
void Test::testTrivial(void (Test::* method)(int*,int,int))
{
static int c;
int a = 1, b = 2;
for (int i = 0; i < n; ++i) {
(this->*method)(&c, a, b);
}
}
void Test::testNonTrivial(void (Test::* method)(QString*, const QString&, const QString&))
{
static QString c;
QString a(500, 'a');
QString b(500, 'b');
for (int i = 0; i < n; ++i) {
(this->*method)(&c, a, b);
}
}
static int pct(int a, int b)
{
return (100.0*a/b) - 100.0;
}
void Test::benchmark(bool timed)
{
const QEventLoop::ProcessEventsFlags evFlags =
QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers;
QTextStream out(stdout);
QElapsedTimer timer;
quint64 t, nt, td, ntd, ts, nts;
if (!timed) out << "Warming up the caches..." << endl;
timer.start();
testTrivial(&Test::trivial);
t = timer.elapsed();
if (timed) out << "trivial direct call took " << t << "ms" << endl;
timer.start();
testNonTrivial(&Test::nonTrivial);
nt = timer.elapsed();
if (timed) out << "nonTrivial direct call took " << nt << "ms" << endl;
QCoreApplication::processEvents(evFlags);
timer.start();
testTrivial(&Test::trivialSignalD);
QCoreApplication::processEvents(evFlags);
td = timer.elapsed();
if (timed) {
out << "trivial direct signal-slot call took " << td << "ms, "
<< pct(td, t) << "% longer than direct call." << endl;
}
timer.start();
testNonTrivial(&Test::nonTrivialSignalD);
QCoreApplication::processEvents(evFlags);
ntd = timer.elapsed();
if (timed) {
out << "nonTrivial direct signal-slot call took " << ntd << "ms, "
<< pct(ntd, nt) << "% longer than direct call." << endl;
}
timer.start();
testTrivial(&Test::trivialSignalQ);
QCoreApplication::processEvents(evFlags);
ts = timer.elapsed();
if (timed) {
out << "trivial queued signal-slot call took " << ts << "ms, "
<< pct(ts, td) << "% longer than direct signal-slot and "
<< pct(ts, t) << "% longer than direct call." << endl;
}
timer.start();
testNonTrivial(&Test::nonTrivialSignalQ);
QCoreApplication::processEvents(evFlags);
nts = timer.elapsed();
if (timed) {
out << "nonTrivial queued signal-slot call took " << ts << "ms, "
<< pct(nts, ntd) << "% longer than direct signal-slot and "
<< pct(nts, nt) << "% longer than direct call." << endl;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test t;
return a.exec();
}
#include "main.moc"
Tôi khuyên bạn nên xem hướng dẫn rất rõ ràng về cách thực sự sử dụng dứt khoát QThread http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the- đầy đủ giải thích/ – linello
Cô ấy nói có khá nhiều những gì tôi nói ở đây ... –
Có một lỗi trong đoạn mã trên, anh ta thử nghiệm lần cuối in thời gian trôi qua (ts) của bài kiểm tra ở trên đó. – Richy