項目中有一個需求就是,由於須要請求服務端數據,由於網絡的讀取會阻塞,因此該過程不能放在Qt中的UI主線程當中,須要用一個後臺線程來讀取數據,數據準備完畢後網絡
在經過Qt5中的信號槽機制來跨線程的傳遞數據。以前的博文使用過moveToThread的方式來說解建立後臺線程,可是如今後臺線程須要與前臺UI線程數據互交,然而,最悲劇的就是信號發出去了,多線程
可是前臺的UI線程對象收不到信號,也就是相應的槽函數沒被調用。以前博文後臺線程是沒有與前臺UI線程互交的,由於它是採集數據的線程,只管往目標地址發送數據就能夠了。可是接收線程就不同了,函數
它須要把後臺接收到的網絡數據放到前臺GUI中展示出來。這不可避免的產生互交和數據的傳遞。this
前臺的UI線程建立後臺線程的代碼大概以下:spa
1 RecvDataObject *recv_obj = new RecvDataObject; 2 3 QThread* backgroundRecvThread = new QThread; 4 5 6 recv_obj->moveToThread(backgroundRecvThread); 7 8 connect(recv_obj, &RecvDataObject::dataRecved, 9 this, &TerminalStatusWidget::slotDataRecved,Qt::QueuedConnection); 10 11 12 backgroundRecvThread->start();
注意,多線程間的信號槽傳遞,在connect的時候須要以Qt::QueuedConnection的方式,否則以Qt::DirectConnection的方式接收者UI線程會很長時間收不到後臺線程發出的信號,或者信號直接丟失都是有可能的。參考線程
http://www.qtcentre.org/threads/17764-emit-qt-signal-is-very-slow-how-it-can-be-optimizedcode
RecvDataObect是用來接收後臺數據的對象被move到了backgroundRecvThread線程中去執行了。其聲明是這樣的:對象
1 class RecvDataObject : public QObject 2 { 3 Q_OBJECT 4 5 public: 6 RecvDataObject(); 7 ~RecvDataObject(); 8 signals: 9 void dataRecved(std::vector<RunTimeInfo> list); 10 public slots: 11 void slotRecvTask(); 12 private: 13 QTimer m_RecvTask; 14 15 };
該類的構造函數我採用了一個Timer來循環執行slotRecvTask()的任務,專門建立網絡鏈接,接收網絡數據。而後數據接收完畢後,經過發送dataRecved的信號傳遞到UI主線程中的slot函數中,可是不能正常工做。槽函數一直不能調用。blog
上網查了緣由才知道,原來Qt的信號槽函數只默認支持Qt的類型和C++提供的內建的基本類型,好比int double float啥的,根本不支持C++的std::string std::vector 自定義的struct類型。因此須要用Qt提供的Q_DECLARE_METATYPE和get
qRegisterMetaType來聲明和註冊自定義的類型和C++的其餘類型。 因此以上的C++類RecvDataObject應該變成如下:
1 Q_DECLARE_METATYPE(RunTimeInfo) 2 Q_DECLARE_METATYPE(std::vector<RunTimeInfo>) 3 4 class RecvDataObject : public QObject 5 { 6 Q_OBJECT 7 8 public: 9 RecvDataObject() 10 { 11 qRegisterMetaType<RunTimeInfo>("RunTimeInfo"); 12 qRegisterMetaType<std::vector<RunTimeInfo>>("std::vector<RunTimeInfo>"); 13 14 m_RecvTask.setInterval(5000); 15 connect(&m_RecvTask, SIGNAL(timeout()), this, SLOT(slotRecvTask())); 16 m_RecvTask.start(); 17 } 18 ~RecvDataObject(); 19 signals: 20 void dataRecved(std::vector<RunTimeInfo> list); 21 public slots: 22 void slotRecvTask(); 23 private: 24 QTimer m_RecvTask; 25 26 };
而後主線程的Widget類的構造函數裏面還必須加入:
1 qRegisterMetaType<RunTimeInfo>("RunTimeInfo"); 2 qRegisterMetaType<std::vector<RunTimeInfo>>("std::vector<RunTimeInfo>");
這樣信號槽函數才能正確工做,經過信號槽機制跨線程的數據傳遞完成了,完美運行。
references:
https://stackoverflow.com/questions/638251/how-to-emit-cross-thread-signal-in-qt
http://www.qtcentre.org/threads/54409-signal-slot-with-std-string-How
https://stackoverflow.com/questions/14083599/signals-and-slots-passing-data