Qt5中運行後臺網絡讀取線程與主UI線程互交

項目中有一個需求就是,由於須要請求服務端數據,由於網絡的讀取會阻塞,因此該過程不能放在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

相關文章
相關標籤/搜索