1. 程式人生 > >Qt中的中訊號槽與非同步呼叫

Qt中的中訊號槽與非同步呼叫

Qt中使用訊號-槽機制處理跨物件之間的呼叫,該機制的好處有:
1. 使得呼叫關係的繫結和解除十分靈活,不必修改類成員函式程式碼
2. 在不暴露更多全域性變數的情況下實現跨名稱空間呼叫
3. 可以多個訊號對應多個槽,也可以訊號之間繫結,對應於GUI中的邏輯很方便
4. 利用Qt::QueuedConnection可以實現非同步呼叫

一般使用

connect(this, SIGNAL(TestSignal()), this, SLOT(TestSlot()), Qt::AutoConnection)

第四個引數一般省略

以下詳細談談第4點:

對於一個多執行緒GUI程式來說,經常遇到的需求是後臺有比較複雜的IO、網路、資料處理邏輯,響應的業務邏輯有多個狀態,並常伴有延時。這種情況下用多執行緒處理比用定時器方便。而一旦使用多執行緒,就涉及到與GUI主執行緒的同步問題。
從開發者的角度,希望使用的是“執行緒透明”的互動機制,即直接跨執行緒修改GUI控制元件屬性;GUI中使用者的輸入事件傳遞到資料處理執行緒中,並在適當時被響應和處理。
我們希望圖形庫提供以上封裝好的方法,不需要開發者手動處理互斥鎖、訊息佇列等。
Qt做到了

在Qt中使用

connect(obj1, SIGNAL(TestSignal()), obj2, SLOT(TestSlot()), Qt::QueuedConnection);

跨越物件非同步呼叫訊號-槽。注意在obj2中必須呼叫訊息迴圈

void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents)

這是因為,Qt非同步訊號槽機制內部基於Event,必須由QObject內建的事件響應函式處理到來的事件,並實際執行槽函式。

Qt中的主要使用以下函式傳送訊息:

//新增訊息到佇列末尾
void ​postEvent(QObject * receiver, QEvent * event, int priority = Qt::NormalEventPriority) //新增訊息到佇列開頭 bool QCoreApplication::​sendEvent(QObject * receiver, QEvent * event)

可以用來發送Qt預定義事件,比如模擬滑鼠點選。也可以自定義事件,自定義事件對應ID要大於1024。

一般而言,我們自定義事件的目的本質上是為了遠端呼叫,故直接使用非同步訊號槽機制即可。除非需要對於傳遞的資料進行復雜的儲存管理,此時再考慮繼承Event包含對應資料結構。

以下程式碼表明,不呼叫訊息迴圈就無法處理非同步訊號槽

//test.h
class AsynchronizedSlotTest : public QObject
{
    Q_OBJECT
public:
    AsynchronizedSlotTest();
    void runTest();

    public slots :
        void TestSlot();
signals:
    void TestSignal();
};
//test.cpp

AsynchronizedSlotTest::AsynchronizedSlotTest() : QObject()
{
    //connect(this, SIGNAL(TestSignal()), this, SLOT(TestSlot()), Qt::AutoConnection);
    connect(this, SIGNAL(TestSignal()), this, SLOT(TestSlot()), Qt::QueuedConnection);

}
void AsynchronizedSlotTest::runTest()
{
    emit TestSignal();
}

int main()
{
    AsynchronizedSlotTest slotTest;
    slotTest.runTest();
    return 0;
}

將第6行改成第5行就可以,因為此時Qt::DirectConnection直接呼叫不依賴訊息迴圈