天天看點

QThread 使用 slot 槽函數

近日,使用qthread,一些問題百思不得其解,看過大牛的文章,恍然大悟啊。

原文 http://hi.baidu.com/dbzhang800/item/c14c97dd15318d17e1f46f41

在文章開始之前加注一點,為和我一樣qt水準不高的朋友提醒一下。qthread::wait(),一直以來我以為它阻塞的是qthread對象,可是我現在明白,原來阻塞的是這個對象所在的線程(通常是主線程)。

bool qthread::wait ( unsigned long time = ulong_max )

blocks the thread until either of these conditions is met:

the thread associated with this qthread object has finished execution (i.e. when it returns from run()). this function will return true if the thread has finished. it also returns true if the thread has not been started yet.

time milliseconds has elapsed. if time is ulong_max (the default), then the wait will never timeout (the thread must return from run()). this function will return false if the wait timed out.

以下紅色部分為我添加。

起源

昨天不小心看到qt開發人員( bradley t. hughes)blog中的一片文章 you are-doing-it-wrong 。 結果看得頭昏腦脹:好歹也自學了近1年的qt,也一直很小心、很認真地閱讀qt和manual和例子等資料,卻被突然告知,qthread的正确使用方法是一種自己從沒見過,而且qt

manual、example、書籍中都沒有提到過的一種方法。到底怎麼了... 

莫非manual、exmaple以及資料中的介紹都是錯的??

認真看看其他的人的評論,總算理清了一點頭緒。所有事情源于 qthread 的事件循環!

qthread 的兩種使用方法

1. 不使用事件循環。這是官方的 manual 、example 以及相關書籍中都介紹的一種的方法。

a. 子類化 qthread

b. 重載 run 函數,run函數内有一個 while 或 for 的死循環

c. 設定一個标記為來控制死循環的退出。

如果使用這一方法,qthread::quit()沒有效果。因為這個線程根本就不需要事件循環。這種情況想退出,直接使用qt很不推薦的terminate().

2. 使用事件循環。(部落格 you are-doing-it-wrong 批駁的就是這種情況下的

一種用法。)

a. 子類化 qthread,

b. 重載 run 使其調用 qthread::exec() 

c. 并為該類定義信号和槽,這樣一來,由于槽函數并不會在新開的 thread 運作,很多人為了解決這個問題在構造函數中調用<code> movetothread(this);  而争論和不解正是這樣的一條語句造成的。</code>bradley t. hughes 給出說明是: qthread 應該被看做是作業系統線程的接口或控制點,而不應該包含需要在新線程中運作的代碼。需要運作的代碼應該放到一個qobject的子類中,然後将該子類的對象movetothread到新線程中。

另外:

在qt4.3(包括)之前,run 是虛函數,必須子類化qthread來實作run函數。

而從qt4.4開始,    ,run 預設調用 qthread::exec() 。這樣一來不需要子類化 qthread 了,隻需要子類化一個 qobject 就夠了,這正是被 bradley t. hughes推薦的方法。

終于看懂了,但

不管怎麼說,都應該是 qthread 當初的設計導緻的這種問題,而所有文檔和例子中都沒有提到該如何使用qthread 進一步加劇了對qthread的這種誤用。

另注:1.qthread對象從建立起就是活躍的,是以大牛bradley t. hughes把qobject對象移動到qthread中,對qobject的操作是完全合理合法合邏輯的。

2.既然使用了多線程,就必須考慮互斥問題,qthread的所有slot函數都是可多重入和不安全的(具體參見qt的可重入和線程安全)。而且在此之外,除了gui類對象必須在主程序(不可重入,進而保證了線程安全),互斥鎖一類的類可重入和線程安全外。是以的qobject對象都不是線程安全的,換句話說,在主線程内為單線程設計的qobject子類對象,如果沒有對其slot函數做互斥處理,就會出現因signal調用而反複重入某個slot函數的情況,反而成了多線程。(c++對象,由于是順序調用,是以在單線程下不會出現這個問題)。這時需要依據情況考慮互斥鎖。

3.使用大牛bradley

t. hughesr的方法把qobject對象移動到qthread中,要使用signal+slot的方式來調用函數,這樣的話,通過qt消息機制,qobject被調用的函數是線上程内執行。如果直接(qobject對象).abc()的話,這個成員函數是在主程序内執行,可能會出現"qobject::killtimer: timers cannot be stopped from another thread"的運作錯誤。

相關連結:

http://labs.qt.nokia.com/blogs/2010/06/17/youre-doing-it-wrong/

http://labs.qt.nokia.com/blogs/2006/12/04/threading-without-the-headache/

http://labs.qt.nokia.com/blogs/2007/07/05/qthreads-no-longer-abstract/

http://gitorious.org/qthreadhowto/qthreadhowto/trees/master

http://blog.exys.org/entries/2010/qthread_affinity.html

http://thesmithfam.org/blog/2010/02/07/talking-to-qt-threads/

繼續閱讀