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

https://www.cnblogs.com/liushui-sky/p/5977194.html

QThread::wait(),一直以来我以为它阻塞的是QThread对象,可是我现在明白,原来阻塞的是这个对象所在的线程(通常是主线程)——所有事情源于 QThread 的事件循环——如果使用继承QThread这一方法,QThread::quit()没有效果,因为这个线程根本就不需要事件循环的更多相关文章

  1. Android中为什么主线程不会因为Looper.loop()方法造成阻塞

    很多人都对Handler的机制有所了解,如果不是很熟悉的可以看看我 如果看过源码的人都知道,在处理消息的时候使用了Looper.loop()方法,并且在该方法中进入了一个死循环,同时Looper.lo ...

  2. python全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程 什么是线程? 线程是cpu调度的最小单位 进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的 ...

  3. Qt自己定义事件实现及子线程向主线程传送事件消息

    近期在又一次学习Qt的时候,由于要涉及到子线程与主线程传递消息,所以便琢磨了一下.顺便把有用的记录下来,方便自己以后查询及各位同仁的參考! 特此声明,本篇博文主要讲述有用的,也就是直接说明怎么实现,就 ...

  4. 【Java面试题】30 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序。

    题目如下: 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次, 接着再回到主线程又循环100,如此循环50次 思路如下: 子线程语主线程为互斥,可用SYNCHRONIZED.很容易想 ...

  5. JS 事件循环机制 - 任务队列、web API、JS主线程的相互协同

    一.JS单线程.异步.同步概念 从上一篇说明vue nextTick的文章中,多次出现“事件循环”这个名词,简单说明了事件循环的步骤,以便理解nextTick的运行时机,这篇文章将更为详细的分析下事件 ...

  6. 通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

    Web Workers API - Web API 接口参考 | MDNhttps://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API ...

  7. 阻塞I/O、非阻塞I/O和I/O多路复用、怎样理解阻塞非阻塞与同步异步的区别?

    “阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答.1.同步与异步 同步和异步关注的是消息通信机制 (syn ...

  8. based on Greenlets (via Eventlet and Gevent) fork 孙子worker 比较 gevent不是异步 协程原理 占位符 placeholder (Future, Promise, Deferred) 循环引擎 greenlet 没有显式调度的微线程,换言之 协程

    gevent GitHub - gevent/gevent: Coroutine-based concurrency library for Python https://github.com/gev ...

  9. python 全栈开发,Day20(object类,继承与派生,super方法,钻石继承)

    先来讲一个例子 老师有生日,怎么组合呢? class Birthday: # 生日 def __init__(self,year,month,day): self.year = year self.m ...

随机推荐

  1. JS 浏览器地址栏传递参数,参数加密/解密(编码/解码)

    我们有时候在JS里进行页面跳转,并且传递了参数(AppName),如下: window.location = "../../views/form/edit.html?AppName=新增&q ...

  2. 2018-2019-2 20165215《网络对抗技术》Exp8 Web基础

    目录 实验内容 基础问题回答 实验步骤 (一)Web前端HTML (二) Web前端javascipt (三)Web后端:MySQL基础:正常安装.启动MySQL,建库.创建用户.修改密码.建表 (四 ...

  3. 【Blog怎么玩】什么叫EntryName友好地址名?

    EntryName友好地址名 其实就是你这个页面的别名,如果设置的好的话,在SEO搜索中,会更清晰的显示出来. 好处1:清晰的URL 比如我有一篇展示可爱兔子的博文,我想让它的网址清晰的显示出来,而不 ...

  4. Mysql的exist与in的区别

    如果查询的两个表大小相当,那么用in和exists差别不大. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:    例如:表A(小表),表B(大表)   1: s ...

  5. JScript 程序流程控制

    Jscript 脚本中的语句一般是按照写的顺序来运行的.这种运行称为顺序运行,是程序流的默认方向. 与顺序运行不同,另一种运行将程序流转换到脚本的另外的部分.也就是,不按顺序运行下一条语句,而是运行另 ...

  6. AMBARI部署HADOOP集群(4)

    通过 Ambari 部署 hadoop 集群 1. 打开 http://192.168.242.181:8080  登陆的用户名/密码是 : admin/admin 2. 点击 “LAUNCH INS ...

  7. 数据分析 - numpy 模块

    numpy 概述 ▨  Numerical Python. 补充了python所欠缺的数值计算能力 ▨  Numpy是其他数据分析及机器学习库的底层库 ▨  Numpy完全标准C语言实现,运行效率充分 ...

  8. ssh 的一个坑

    概述 今天我碰到 fabric 和 ssh 的一个坑,记录下来,供以后开发时参考,相信对其他人也有用. ssh 今天用 ssh 登录远程服务器用不了 npm,查了下,发现原因是: ssh登录时不会加载 ...

  9. 使用Navicat Premium 客户端绕过白名单限制mysql数据库

    针对有些数据库有白名单限制,但如果IP经常浮动的话,会要经常加白名单,但如果知道可以连接数据库的linux用户密码就能 通过SSH通道代理来连接数据库.保存密码后,这样就能直接连接数据库,减省很多麻烦 ...

  10. Python中将(字典,列表等)变量格式化成字符串输出

    比如原始的List变量的值是这种: [{"]}] 而想要将其输出为带缩进的,树状的,很漂亮的效果,那么可以通过这样的方法: import json #demoDictList is the ...