2012-05-31 78 views

回答

55

首先,你應該把任何插槽QThreads。 QThreads並非真正意味着通過重新實現run方法和私有方法(而不是信號!)。

甲QThread的在概念上是線程控制器,而不是一個線程本身。在大多數情況下,你應該處理QObject。啓動一個線程,然後將該對象實例移動到該線程。這是你在線程中正確工作的唯一方法。將線程實例(它是 QObject派生的!)移動到線程是一種黑客和糟糕的風格。儘管不知情的論壇帖子告訴我們不要這樣做。

至於你的問題的其餘部分:信號槽調用沒有找到任何東西,也驗證了。連接建立後,「位置」和「確認」完成。在通話時完成的主要步驟如下:

  1. 鎖定池中的信號插槽互斥鎖。

  2. 通過連接列表進行遍歷。

  3. 執行使用直接或排隊的呼叫呼叫。

公共成本

任何信號槽調用總是開始爲在MOC產生的信號的實現直接通話。信號的指向參數的數組構造在堆棧上。參數不會被複制。

該信號然後調用QMetaObject::activate,其中獲取連接列表互斥,並且迭代連接的時隙列表,爲每個時隙發出呼叫。

直接連接

並非被那裏進行,如果QMetaObject::connect用於建立連接的插槽是通過在已建立連接,或QObject::qt_metacall時獲得的直接調用QObject::qt_static_metacall調用。後者允許dynamic creation of signals and slots

排隊連接

的參數必須編組和複製,由於呼叫必須被存儲在事件隊列和信號必須返回。這是通過分配一個指向拷貝的指針數組來完成的,並且在堆上拷貝每個參數。這樣做的代碼實際上沒有簡單的陳舊的C。

呼叫的排隊在queued_activate內完成。這是複製構建完成的地方。

排隊呼叫的開銷總是至少有一個堆分配QMetaCallEvent。如果調用有任何參數,則會分配一個指向參數的數組,併爲每個參數分配一個額外的分配。對於具有n參數的調用,作爲C表達式給出的成本是(n ? 2+n : 1)分配。阻止調用的返回值是作爲參數的計數器。可以說,Qt的這一方面可以優化到一切的一個分配,但在現實生活中,只要你調用簡單的方法就可以。

基準測試結果

即使直接(非排隊)信號槽調用有一個可衡量的開銷,但你要選擇你的戰場。易於構建代碼與性能。您確實測量了最終應用程序的性能並確定了瓶頸,是嗎?如果這樣做,您可能會發現在實際應用中,信號插槽開銷不起作用。

唯一的時間信號時隙機制有顯着的開銷是如果你打電話瑣碎的功能。說,如果你打電話trivial插槽在下面的代碼。這是一個完整的,獨立的基準測試,所以請隨時運行並親自查看。我的機器上的結果是:

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. 

應注意什麼,也許,是連接字符串是相當快的:)

請注意,我通過一個函數指針做呼叫,這是爲了防止編譯器優化對加法函數的直接調用。

//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" 
+4

我會建議看看非常明確的教程,如何真正明確地使用QThread http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-完整解釋/ – linello

+0

她說這裏有很多我在這裏說的...... –

+0

上面的代碼中有一個錯誤,他上次測試打印上面的測試的經過時間(ts)。 – Richy

4

Ofcourse它們影響應用性能,這主要是由於所用的時間超過定位連接物體+驗證所述槽對象狀態N所以。但信號和槽機構的簡單性和靈活性是非常值得的開銷。 Plus one of the major advantage of signal-slot mechanism is they are type=safe allowing communication between objects, irrespective of type of object unlike callbacks.

與回調相比,信號和時隙由於提供的靈活性增加而稍慢,但實際應用的差異並不明顯。一般來說,發送連接到某些時隙的信號比使用非虛函數調用直接調用接收器慢大約十倍。這是定位連接對象所需的開銷,要安全地迭代所有連接(即檢查後續接收器在發射期間是否未被破壞),並以通用方式編組任何參數。雖然十個非虛函數調用可能聽起來很多,但它比任何新的或刪除操作的開銷少得多。只要你執行幕後操縱者需要新的或刪除字符串,向量或列表操作,信號和槽的開銷只爲全功能通話費用的一個非常小的比例 負責。

來源:Signals and Slots

+0

你確定開銷因子是低?信號調用QMetaObject :: activate,它有大約一百行代碼。我猜測它比插槽的直接非虛擬呼叫慢100倍。但我同意你的看法:在絕大多數情況下,這種開銷並不重要。 – leemes

+0

單線程應用程序中現代內存分配器上的新操作或刪除操作的開銷很小。很小。實際上這麼小,將兩個1000字符的QStrings連接成一個新的QString需要的時間與直接的信號插槽連接開銷一樣多! –

相關問題