QSerialPort適應多執行緒應用的改進

2021-07-07 03:15:10 字數 1549 閱讀 4094

類unix系統的裝置介面使用了基於select的事件驅動,這使得裝置物件必須存在於某乙個執行緒中,而因為select事件無法直接從裝置跨執行緒傳輸,雙工裝置的跨執行緒操作也無法直接實現。qt作為跨平台的開發庫,為相容類unix系統的事件驅動,也設計為類似的限制。

對於全雙工串列埠的qserialport物件來說,當資料傳輸壓力較小時,直接在主線程中通過為readyread訊號編寫槽函式可以很方便的實現資料接收,而主線程中與ui互動相關的資料傳送工作也可以很好的執行;而當某一方向的資料傳輸壓力較大時,由於主線程還必須響應ui的事件迴圈,這可能會造成該方向的資料傳輸產生延遲,因此必須移入子執行緒以避開主線程的ui事件迴圈,也就是說,雙工裝置的跨執行緒操作是必要的。

那麼如何設計基於qt實現雙工裝置的跨執行緒操作呢?以串列埠為例,可以考慮使用qt的阻塞式跨執行緒訊號槽連線機制,在操作發起執行緒中發射訊號傳入引數後等待槽函式執行完成;而在串列埠的訊號連線的槽函式中呼叫對應的操作函式,並將函式結果存入類成員變數作為中轉,然後返回;操作發起執行緒阻塞結束後,通過讀取類成員變數來獲取並輸出操作結果。

以串列埠的write操作示例具體的實現方法,新的方法命名為threadsafewrite,而從qserialport繼承的自定義類命名為qthreadsafeserialport:

class qthreadsafeserialport : public qserialport

, qt::blockingqueuedconnection);

}qint64 threadsafewrite(char

const* data) else

}

由於使用了訊號槽機制,qthreadsafeserialport 物件所在的執行緒必須使用事件迴圈,否則跨執行緒的threadsafewrite呼叫仍然無法正確執行;而為了執行事件迴圈,qthread類的run函式中必須執行exec();exec()在事件迴圈結束之前不會返回,這使得在run函式中執行阻塞式資料接收或傳送的方案變得無法實現,因此仍然不得不通過實現槽函式並連線readyread/byteswritten訊號來實現資料接收/傳送。為了讓資料接收/傳送的槽函式在子執行緒中執行,槽函式所依附的qobject繼承類物件必須生存在子執行緒中。示例**如下:

class threadslothelper : public qobject

public q_slots:

void onreadyread(void)

};class examplethread : public qthread

public:

examplethread(qobject* parent = 0) : qthread(parent)

};

效能分析:雖然子執行緒中使用了事件迴圈,但由於子執行緒中並沒有ui,不用執行ui事件,子執行緒所有的事件迴圈均可用於串列埠的通訊事件,因而子執行緒中的效能損失並不明顯;跨執行緒的threadsafewrite呼叫由於以阻塞式的訊號連線方式連線訊號和槽函式,需要跨執行緒等待事件迴圈的執行,其執行效率必然較為低下,這是使用跨執行緒訊號槽機制所必須付出的代價;若threadsafewrite的實現方案無法滿足效能要求,則必須重新設計資料讀寫方案了。

ThreadPoolExecutor 多執行緒

from concurrent.futures import threadpoolexecutor,wait,all completed from queue import queue myqueue queue 佇列,用於儲存函式執行結果。多執行緒的問題之一 如何儲存函式執行的結果。def thr...

c 多線例項

using system using system.threading using system.text namespace controlthread 第二個執行緒正在執行,請輸入 s uspend,r esume,i nterrupt,or e xit.datetime.now.tostrin...

CLLocationManager在多執行緒下使用

似乎定位的返回 呼叫 只能有主線程來呼叫,並且這個物件還必須是在主線程建立的。做過以下實驗 1.子執行緒中 self.locationmanager cllocationmanager alloc init autorelease locationmanager.delegate self loca...