QThread 的使用方法

近日,使用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 运行,很多人为了解决这个问题在构造函数中调用  moveToThread(this); 

而争论和不解正是这样的一条语句造成的。

Bradley T. Hughes 给出说明是:  QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。

另外:
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。
而从Qt4.4开始, qthreads-no-longer-abstract     ,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/


你可能感兴趣的:(qt,qthread)