近日,使用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/

 
转自:http://blog.csdn.net/lainegates/article/details/9701215
 
 

QThread实例代表一个线程,我们可以重新实现QThread::run(),要新建一个线程,我们应该先继承QThread并重新实现run()函数。

需要注意的是:

1.必须在创建QThread对象之前创建 QApplication (或QCoreApplication)对象。

2. QCoreApplication::exec() 必须只能从主线程调用,不能从QThread线程调用。

class MyThread : public  QThread

{

Q_OBJECT

public:

MyThread(QObject *parent = NULL);

~MyThread();

protected:

voidrun();

};

void MyThread::run()

{

for( int count = 0; count < 20; count++ )

{

sleep( 1 );

}

qDebug( "finish!");

}

我们可以在另外的函数这样调用:

MyThread thread;

thread.start();

thread.wait();   //必须要加的语句,等待thread结束。

当然我们先开的线程可能是要运行很久,会卡住主线程,使用QeventLoop就可以轻松解决此类问题:

MyThread thread;

thread.start();

QeventLoop;

connect(&thread,SIGNAL(finished ()),&eventLoop,SLOT(quit()));

thread.wait(1);

eventLoop.exec();

接下来我们看QThread的其他属性和函数:

Qthread的优先级属性:Priority指示系统如何调度线程。

Constant

Value

Description

QThread::IdlePriority

0

scheduled only when no other threads are running.

QThread::LowestPriority

1

scheduled less often than LowPriority.

QThread::LowPriority

2

scheduled less often than NormalPriority.

QThread::NormalPriority

3

the default priority of the operating system.

QThread::HighPriority

4

scheduled more often than NormalPriority.

QThread::HighestPriority

5

scheduled more often than HighPriority.

QThread::TimeCriticalPriority

6

scheduled as often as possible.

QThread::InheritPriority

7

use the same priority as the creating thread. This is the default.

0到6的优先级跟windows线程相对应的,就多了一个InheritPriority。

非静态成员函数:

void

exit ( int returnCode = 0 )

该函数告诉线程退出事件循环和返回代码returnCode ;通常返回0意味着成功,非0意味着错误。

需要注意的是,该函数不像C语言的库函数exit一样返回到调用者。除非再次调用QThread::exec(),否则不会再执行任何事件循环。如果QThread::exec()的事件循环没有运行,接着的QThread::exec()将会立即返回。

这两个函数看名字就知道意思:

bool QThread::isFinished ()const

如果线程结束,则返回true,否则返回false。

bool QThread::isRunning ()const

如果线程还在运行则返回true,否则返回false

void QThread::setPriority ( Priority priority )

该函数设置了正在运行的线程的优先级。如果线程没有运行,函数什么也不做,直接返回。跟踪源码发现setPriority 的源码部分如下:

if(!d->running)

{

qWarning("QThread::setPriority:Cannot set priority, thread is not running");

return;

}

如果线程没有运行,函数只输出一个警告便返回了。

需要注意的是:参数不能是InheritPriorty。

case InheritPriority:

default:

qWarning("QThread::setPriority:Argument cannot be InheritPriority");

return;

如果是InheritPriorty,函数也只输出一个警告便返回了。

优先级参数的影响依赖于操作系统的调度策略。特别是,在不支持线程优先级的系统(如Linux)优先级参数将被忽略。

void QThread::setStackSize ( uint stackSize )

该函数设置线程堆栈的最大值。

需要注意的是:

1.必须在线程启动之前设置

2. 虽然参数是uint类型,传入负数的话会自动转换。

3.大部分的操作系统都设置了线程堆栈的最大最小值限制。如果超出了限制线程可能运行失败。

验证了一下,当我传-1进去,stackSize值变成了4294967295。Start函数里调用beginthreadex创建新线程。Beginthreadex里调用CreateThread这是大部分用过windows多线程的程序员都了解的。创建失败则输出警告并返回。

d->handle = (Qt::HANDLE)_beginthreadex(NULL, d->stackSize, QThreadPrivate::start,

this, CREATE_SUSPENDED, &(d->id));

if(!d->handle) {

qErrnoWarning(errno, "QThread::start: Failed to create thread");

d->running = false;

d->finished = true;

return;

}

uint QThread::stackSize () const

该函数返回线程堆栈最大值,如果调用setStackSize()设置最大值的话,返回的就是设置的值,否则返回0;

bool QThread::wait ( unsigned long time =ULONG_MAX )

该函数阻塞线程直到满足以下条件之一;

1.      线程已经执行结束。如果线程结束,该函数返回true。如果线程没被启动也会返回true。

2.      Time毫秒时间过去了。如果time 为 ULONG_MAX,该函数永远不会超时。如果超时,函数返回false。

槽函数:

void QThread::quit ()

函数退出事件循环,返回代码为0.与调用exit(0).一样的效果。如果线程没有事件循环则什么也不做。

void QThread::start ( Priority priority =InheritPriority ) [slot]

通过调用开始run(),执行线程。如果线程已经在运行,则什么也不做。

看一下源码:

Start()调用:

d->handle =(Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start, this,CREATE_SUSPENDED, &(d->id));

创建线程。

unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSEQThreadPrivate::start(void *arg)

{

QThread *thr = reinterpret_cast<QThread*>(arg);

QThreadData *data = QThreadData::get2(thr);

。。。。。

。。。。。。          //中间省略,删掉了

emitthr->started();       //发送started信号。

QThread::setTerminationEnabled(true);

thr->run();              //运行我们重新实现的代码。

finish(arg);              //该函数发送finished();信号。

return 0;

}

void QThread::terminate ()

终止线程的运行。线程可能不会理解被终止,这依赖于操作系统的调用策略。在terminate()之后调用QThread::wait()同步终止。

终止线程之后,所有等待线程结束的线程都会被唤醒。

void QThread::terminate()

{

Q_D(QThread);

QMutexLocker locker(&d->mutex);

if(!d->running)

return;

if(!d->terminationEnabled) {

d->terminatePending = true;

return;

}

TerminateThread(d->handle, 0);

d->terminated = true;

QThreadPrivate::finish(this, false);            //finish函数发送了finished()信号。从而会唤醒所有等待的线程。

}

除非真的有必要,不然不要轻易调用该函数。

QThread * QThread::currentThread ()

函数返回当前正在执行的线程。

Qt::HANDLE QThread::currentThreadId ()

注意:

返回当前运行的线程的句柄。该函数返回的句柄只能内部使用,不能在其他任何程序的代码中使用。

在windows系统,该返回值是当前线程的伪句柄。不能用作数值比较,函数返回Win32 函数 getCurrentThreadId()返回的DWORD值。而不是 Win32 函数 getCurrentThread()返回的HANDLE值。

int QThread::idealThreadCount ()

函数返回系统可以运行的理想的线程数。

int QThread::idealThreadCount()

{

SYSTEM_INFO sysinfo;

GetSystemInfo(&sysinfo);

returnsysinfo.dwNumberOfProcessors;    //返回系统中的处理器的数目。

}

void QThread::yieldCurrentThread ()

放弃当前线程转到另外可执行的线程,有系统决定转到哪个线程。

void QThread::sleep ( unsigned long secs )

void QThread::msleep ( unsigned long msecs )

void QThread::usleep ( unsigned long usecs )

这三个函数是让线程进入休眠状态。它们内部都是调用了windows API Sleep。

void QThread::setTerminationEnabled ( bool enabled =true )

设置是否可以终止当前线程。

转自:http://blog.csdn.net/hai200501019/article/details/9535069

QThread 的使用方法及函数解析的更多相关文章

  1. PHP json_decode 函数解析 json 结果为 NULL 的解决方法

    在做网站 CMS 模块时,对于模块内容 content 字段,保存的是 json 格式的字符串,所以在后台进行模块内容的编辑操作 ( 取出保存的数据 ) 时,需要用到 json_decode() 函数 ...

  2. [转]javascript eval函数解析json数据时为什加上圆括号eval("("+data+")")

    javascript eval函数解析json数据时为什么 加上圆括号?为什么要 eval这里要添加 “("("+data+")");//”呢?   原因在于: ...

  3. Matlab中bsxfun和unique函数解析

    一.问题来源 来自于一份LSH代码,记录下来. 二.函数解析 2.1 bsxfun bsxfun是一个matlab自版本R2007a来就提供的一个函数,作用是”applies an element-b ...

  4. socket使用TCP协议时,send、recv函数解析以及TCP连接关闭的问题

    Tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据. 在阻塞模式下, send函数的过程是将应用程序请求发送的数 ...

  5. driver_register()函数解析

    driver_register()函数解析 /** * driver_register - register driver with bus * @drv: driver to register *  ...

  6. Loadrunner Http接口Get/Post方法性能测试脚本解析

    最近使用LoadRunner 11进行了一次完整的Http WEB接口性能测试,下面介绍下Http接口Get/Post方法性能测试脚本通用编写方法. 1. Http接口性能测试基本流程 首先定义了一个 ...

  7. async函数解析

    转载请注明出处:async函数解析 async函数是基于Generator函数实现的,也就是说是Generator函数的语法糖.在之前的文章有介绍过Generator函数语法和异步应用,如果对其不了解 ...

  8. erlang下lists模块sort(排序)方法源码解析(二)

    上接erlang下lists模块sort(排序)方法源码解析(一),到目前为止,list列表已经被分割成N个列表,而且每个列表的元素是有序的(从大到小) 下面我们重点来看看mergel和rmergel ...

  9. erlang下lists模块sort(排序)方法源码解析(一)

    排序算法一直是各种语言最简单也是最复杂的算法,例如十大经典排序算法(动图演示)里面讲的那样 第一次看lists的sort方法的时候,蒙了,几百行的代码,我心想要这么复杂么(因为C语言的冒泡排序我记得不 ...

随机推荐

  1. 基于NOPI的Execl模板转换类,直接将Execl模板转换对应的Entity

    1.创建实体属性标记 public class CellAttribute : Attribute { /// <summary> /// /// </summary> /// ...

  2. OAF_OAF控件系列7 - Tree的实现(案列)

    2014-06-02 Created By BaoXinjian

  3. android侧滑效果,SlidingMenu配置

    最近开始做点东西,需要用到android activity侧滑的效果.感觉配置起来还是有点小麻烦,总结一下以便回顾. 需要的东西: 1.SlidingMenu项目:点击打开链接 2.ActionBar ...

  4. ubuntu16安装配置nginx

    Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. Nginx 是由 Igor Sysoev ...

  5. MySQL数据分组GROUP BY 和HAVING

    对于分组的理解,可以这样:对GROUP BY子句后面跟随的列名进行分组,然后对每一个分组而不是整个表进行操作. 举例如下:在产品表中,检索每一个供应商提供的商品的数量. mysql> SELEC ...

  6. git版本控制文件提交到composer应用市场,并下载用市场的软件库

    要把github中的项目提交到composer中去,必须在github管理的项目中新建对应的composer.json文件, composer.json文件建立的方法 cmd定位到项目目录 compo ...

  7. 【Android】8.1 主题基本用法

    分类:C#.Android.VS2015: 创建日期:2016-02-17 一.创建本章示例主界面 1.界面截图 2.MainActivity.cs文件中对应的代码 在CreateChItems()方 ...

  8. 【Android】6.2 AlertDialog(警告对话框)

    分类:C#.Android.VS2015: 创建日期:2016-02-08 一.简介 AlertDialog也是Android系统当中常用的对话框之一. 在一个AlertDialog中,可以有一个Bu ...

  9. 解决Alt+/不弹出提示的问题

    依次打开eclipse上面的windows ——preferences ——General —— Keys, 在Scheme的下面有一个搜索框,在搜索框里面输入“Content asist”(我的Ec ...

  10. C# 获取FormData数据

    通常的方法是你创建一个 FormData 对象.然后你使用append方法来加入任何额外的key和他们的值.就像这样: var form = new FormData(); form.append(& ...