Qt5中運行后台網絡讀取線程與主UI線程互交


項目中有一個需求就是,因為需要請求服務端數據,因為網絡的讀取會阻塞,所以該過程不能放在Qt中的UI主線程當中,需要用一個后台線程來讀取數據,數據准備完畢后

在通過Qt5中的信號槽機制來跨線程的傳遞數據。之前的博文使用過moveToThread的方式來講解創建后台線程,但是現在后台線程需要與前台UI線程數據互交,然而,最悲劇的就是信號發出去了,

但是前台的UI線程對象收不到信號,也就是相應的槽函數沒被調用。之前博文后台線程是沒有與前台UI線程互交的,因為它是采集數據的線程,只管往目標地址發送數據就可以了。但是接收線程就不一樣了,

它需要把后台接收到的網絡數據放到前台GUI中展現出來。這不可避免的產生互交和數據的傳遞。

 

前台的UI線程創建后台線程的代碼大概如下:

 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-optimized

 

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函數中,但是不能正常工作。槽函數一直不能調用。

上網查了原因才知道,原來Qt的信號槽函數只默認支持Qt的類型和C++提供的內建的基本類型,比如int double float啥的,根本不支持C++的std::string std::vector 自定義的struct類型。所以需要用Qt提供的Q_DECLARE_METATYPE和

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM