Qt多線程編程中子線程如何調用主線程中的成員變數?
最近要編寫一個多線程程序,以前用MFC感覺還挺方便,可以在主窗口類中聲明一個靜態線程函數,後面直接開始線程,並且在次線程的實現函數中通過LPVOID lpParam就可以實現將主線程類中的成員變數傳遞給次線程。
現在我用Qt,想在次線程中調用主線程(主線程是由QDialog繼承過來的)類中的成員變數,卻怎麼也不成功,到底應該如何實現主線程與次線程的變數共享呀,如果都要用信號和槽,那豈不是太麻煩;我試過在主線程中將要在次線程中用到的成員變數聲明為static靜態變數,不過還是沒有成功。
如果是寫操作,請用信號槽。不用信號槽的話,需要加鎖,會影響性能。
子線程中對象emit信號。主線程中對象捕獲後執行相關操作。如果是單純的讀操作,很簡單啊,你只要持有目標對象指針,目標對象寫個type getXXX() const;方法,直接調用就ok了,和跨不跨線程沒任何關係。
另外,請勿跨線程式控制制QWidget及其子對象,Qt不允許在非主線程式控制制GUI。
準確的說,QWidget, QTimer, QTjread, QIODevice對象,都有部分api不允許跨線程操作,否則會執行失敗,控制台會有錯誤日誌。跨線程操作對象,這其實是個很樸素的所有權概念,只不過是邏輯意義上的所有權。
可以參照智能指針的內存所有權概念來類比,那個是實際意義上的所有權。跨所有權的操作,只有一步到位的讀操作是安全的,iterator這種多步執行的都可能遇到迭代器失效。而寫操作是不安全的,必須通過加鎖等代價很高的方式來進行。因此,所有權是最重要的安全線越過了這條線,沒人能保證你的安全,你只能付出極高的代價來實現,而且從邏輯層面上就是不合法的,嚴重破壞了封裝性,提高了耦合度。
最優解,是退到安全線後,也就是用非同步操作來實現所有寫操作,如Qt的信號槽。只有在有極端實時需求時,才考慮直接越過安全線的暴力同步操作。
========================
========以下內容毀三觀================================
然而,同步操作依舊保證不了實時性!
然而,同步操作依舊保證不了實時性!然而,同步操作依舊保證不了實時性!跨線程的同步寫操作,必須加鎖。加鎖就會存在競爭,就會存在阻塞,該發生延遲的一樣發生延遲。
如果你通過優化架構邏輯和代碼演算法,讓兩者儘可能不發生競爭了呢?那非同步同樣沒延遲了!非同步操作是如何實現的?
無非是把你的請求推送到對方線程的事件隊列排隊執行。這個也是跨線程寫,只不過用了比如原子操作來實現了一個無鎖隊列。當有競爭時,同步操作要阻塞等待,非同步操作要隊列等待,本質相同。
當沒有競爭時,同步操作直接完成,非同步操作入隊後直接就出隊了也是直接完成,沒啥區別。
當然,同步操作里,發起方和執行方都在同一線程,不用擔心對方會因線程調度無法實時響應。
然而發起方你自己也是線程,你自己也會被調度走的!在你加鎖等待時,對方的線程也可能被調度走了導致你在空轉的!因此,只有在和系統,和硬體打交道時,由驅動層,由內核態發起中斷或者回調,這時同步操作才有意義,因為可以保證發起者不會被調度走。
但依舊規避不了第二個問題——持有鎖的目標線程被調度走後,你依舊只能在鎖面前乾等著。所以,只要涉及到多線程,我不覺得同步操作的實時性有本質上的優勢。
並且,只要不是做嵌入式,用戶場合里99%代碼都是全程在用戶態執行。這時用同步操作,除了提高耦合,我不覺得有任何優勢。
不然為毛js等語言紛紛要搞async/await,甚至連c++都在做提案了?
以前不是用回調寫出了從操作系統到驅動程序到web應用的整個世界么?原因還不是同步太違背工程學原理了么。
同步操作,歸根結底是為了解決一種反人類的寫法的——目標線程用while(true)處理任務,我要插隊進去只能強行同步調用。
然而在非同步編程里,即使是常駐操作也不會是while(true)。全都可以用事件系統進行事件響應。比如網路服務端,不需要while檢查請求,只需要監聽socket的連接請求,接收請求就行了。在處理請求時,同步模型里是加上鎖的,無法響應同步調用。非同步模型是線程在忙,調用事件排隊等待。在沒處理請求時,同步模型可以直接拿到鎖開始操作。非同步模型下事件隊列為空,來了新指令也能直接響應。如果沒外部請求,也沒調用操作時,同步模型還得while(true)空轉,非同步模型線程直接停了,來事件時才會喚醒,豈不美哉?所以,同步操作的那把鎖,只不過是心理安慰,罷了。CPP又不是默認tls,只要變數在函數的作用域中,都可以調用…只是跨線程注意下線程安全…話說,你用mfc可以用void *把參數傳進去,qt就不行了?
個人建議,你還是先別糾結什麼qt,mfc了,補補cpp和線程的基礎吧…
推薦閱讀: