Qt对线程提供了支持,基本形式有独立于平台的线程类、线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法。

警告:所有的GUI类(比如,QWidget和它的子类),操作系统核心类(比如,QProcess)和网络类都不是线程安全的。

QRegExp使用一个静态缓存并且也不是线程安全的,即使通过使用QMutex来保护的QRegExp对象。

启用线程支持
在Windows上安装Qt时,在一些编译器上线程支持是一个选项。
在Mac OS X和Unix上,线程支持可以当你在运行configure脚本时添加-thread选项就可以生效了。在Unix平台上,多线程程序必须用特殊的方式连接,比如使用特殊的libc,安装程序将会创建另外一个库libqt-mt并且因此线程程序必须和这个库进行连接(使用-lqt-mt)而不是标准的Qt库。
在两个平台上,你都应该定义宏QT_THREAD_SUPPORT来编译(比如,编译时使用-DQT_THREAD_SUPPORT)。在Windows上,这个通常可以在qconfig.h写一个条目来解决。

线程类
最重要的类是QThread,也就是说要开始一个新的线程,就是开始执行你重新实现的QThread::run()。这和Java的线程类很相似。
为了写线程程序,在两个线程同时希望访问同一个数据时,对数据进行保护是很必要的。因此这里也有一个QMutex类,一个线程可以锁定互斥量,并且在它锁定之后,其它线程就不能再锁定这个互斥量了,试图这样做的线程都会被阻塞直到互斥量被释放。例如:

    class MyClass
{
public:
void doStuff( int ); private:
QMutex mutex;
int a;
int b;
}; // 这里设置a为c,b为c*2。 void MyClass::doStuff( int c )
{
mutex.lock();
a = c;
b = c * 2;
mutex.unlock();
}

这保证了同一时间只有一个线程可以进入MyClass::doStuff(),所以b将永远等于c * 2。

另外一个线程也需要在一个给定的条件下等待其它线程的唤醒,QWaitCondition类就被提供了。线程等待的条件QWaitCondition指出发生了什么事情,阻塞将一直持续到这种事情发生。当某种事情发生了,QWaitCondition可以唤醒等待这一事件的线程之一或全部。(这和POSIX线程条件变量是具有相同功能的并且它也是Unix上的一种实现。)例如:

    #include <qapplication.h>
#include <qpushbutton.h> // 全局条件变量
QWaitCondition mycond; // Worker类实现
class Worker : public QPushButton, public QThread
{
Q_OBJECT public:
Worker(QWidget *parent = 0, const char *name = 0)
: QPushButton(parent, name)
{
setText("Start Working"); // 连接从QPushButton继承来的信号和我们的slotClicked()方法
connect(this, SIGNAL(clicked()), SLOT(slotClicked())); // 调用从QThread继承来的start()方法……这将立即开始线程的执行
QThread::start();
} public slots:
void slotClicked()
{
// 唤醒等待这个条件变量的一个线程
mycond.wakeOne();
} protected:
void run()
{
// 这个方法将被新创建的线程调用…… while ( TRUE ) {
// 锁定应用程序互斥锁,并且设置窗口标题来表明我们正在等待开始工作
qApp->lock();
setCaption( "Waiting" );
qApp->unlock(); // 等待直到我们被告知可以继续
mycond.wait(); // 如果我们到了这里,我们已经被另一个线程唤醒……让我们来设置标题来表明我们正在工作
qApp->lock();
setCaption( "Working!" );
qApp->unlock(); // 这可能会占用一些时间,几秒、几分钟或者几小时等等,因为这个一个和GUI线程分开的线程,在处理事件时,GUI线程不会停下来……
do_complicated_thing();
}
}
}; // 主线程——所有的GUI事件都由这个线程处理。
int main( int argc, char **argv )
{
QApplication app( argc, argv ); // 创建一个worker……当我们这样做的时候,这个worker将在一个线程中运行
Worker firstworker( 0, "worker" ); app.setMainWidget( &worker );
worker.show(); return app.exec();
}

只要你按下按钮,这个程序就会唤醒worker线程,这个线程将会进行并且做一些工作并且然后会回来继续等待被告知做更多的工作。如果当按钮被按下时,worker线程正在工作,那么就什么也不会发生。当线程完成了工作并且再次调用QWaitCondition::wait(),然后它就会被开始。

线程安全的事件传递
在Qt中,一个线程总是一个事件线程——确实是这样的,线程从窗口系统中拉出事件并且把它们分发给窗口部件。静态方法QThread::postEvent从线程中传递事件,而不同于事件线程。事件线程被唤醒并且事件就像一个普通窗口系统事件那样在事件线程中被分发。例如,你可以强制一个窗口部件通过如下这样做的一个不同的线程来进行重绘:

    QWidget *mywidget;
QThread::postEvent( mywidget, new QPaintEvent( QRect(0, 0, 100, 100) ) );

这(异步地)将使mywidget重绘一块100*100的正方形区域。

Qt库互斥量
Qt库互斥量提供了从线程而不是事件线程中调用Qt方法的一种方法。例如:

  QApplication *qApp;
QWidget *mywidget; qApp->lock(); mywidget->setGeometry(0,0,100,100); QPainter p;
p.begin(mywidget);
p.drawLine(0,0,100,100);
p.end(); qApp->unlock();

在Qt中没有使用互斥量而调用一个函数通常情况下结果将是不可预知的。从另外一个线程中调用Qt的一个GUI相关函数需要使用Qt库互斥量。在这种情况下,所有可能最终访问任何图形或者窗口系统资源的都是GUI相关的。使用容器类,字符串或者输入/输出类,如果对象只被一个线程使用就不需要任何互斥量了。

当进行线程编程时,需要注意的一些事情:

当使用Qt库互斥量的时候不要做任何阻塞操作。这将会冻结事件循环。
确认你锁定一个递归QMutex的次数和解锁的次数一样,不能多也不能少。
在调用除了Qt容器和工具类的任何东西之前锁定Qt应用程序互斥量。
谨防隐含地共享类,你应该避免在线程之间使用操作符=()来复制它们。这将会在Qt的未来主要的或次要的发行版本中进行改进。
谨防那些没有被设计为线程安全的Qt类,例如,QPtrList的应用程序接口就不是线程安全的并且如果不同的线程需要遍历一个QPtrList,它们应该在调用QPtrList::first()之前锁定并且在到达终点之后解锁,而不是在QPtrList::next()的前后进行锁定和解锁。
确认只在GUI线程中创建的继承和使用了QWidget、QTimer和QSocketNotifier的对象。在一些平台上,在某个不是GUI线程的线程中创建这样的对象将永远不会接受到底层窗口系统的事件。
和上面很相似,只在GUI线程中使用QNetwork类。一个经常被问到的问题是一个QSocket是否可以在多线程中使用。这不是必须得,因为所有的QNetwork类都是异步的。
不要在不是GUI线程的线程中试图调用processEvents()函数。这也包括QDialog::exec()、QPopupMenu::exec()、QApplication::processEvents()和其它一些。
在你的应用程序中,不要把普通的Qt库和支持线程的Qt库混合使用。这也就是说如果你的程序使用了支持线程的Qt库,你就不应该连接普通的Qt库、动态的载入普通Qt库或者动态地连接其它依赖普通Qt库的库或者插件。在一些系统上,这样做会导致Qt库中使用的静态数据变得不可靠了。

Qt入门(9)——Qt中的线程支持的更多相关文章

  1. Qt入门学习——Qt 5 帮助文档的使用

    Qt入门学习——Qt 5 帮助文档的使用 学习图形界面开发,肯定离不开帮助文档的使用,因为它不像 C 语言那样就那么几个函数接口,图形接口的接口可以用海量来形容,常用的我们可能能记住,其它的真的没有必 ...

  2. Qt入门——使用QT+VS2008开发windows应用程序

    1.文件->新建->项目 Qt4 Projects 右边已安装模板当中选择At Application. 确定 2.选择需要使用的QT库 下一步 3. “class name”:指定类的名 ...

  3. 【Qt开发】事件循环与线程 二

    事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...

  4. QT中的线程与事件循环理解(2)

    1. Qt多线程与Qobject的关系 每一个 Qt 应用程序至少有一个事件循环,就是调用了QCoreApplication::exec()的那个事件循环.不过,QThread也可以开启事件循环.只不 ...

  5. Qt中暂停线程的执行(主线程和工作线程共用一把锁,一旦主线程将它锁上,工作线程就无法运行了,这也是一个办法)

    在线程中定义一个信号量: QMutex pause; 把run()函数中循环执行的部分用信号量pause锁住:   void run()   {   while(1)   {   pause.lock ...

  6. qt 如何使用 lamda 表达式接收线程中发射的数据,并在里面更新 UI ?

    Qt 信号和槽连接方式 常量 描述 Qt::AutoConnection (默认)如果接收方位于发出信号的线程中,则使用Qt::DirectConnection.否则,使用Qt::QueuedConn ...

  7. 第15.28节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QTableWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 表格部件为应用程序提供标准的表格显示工具,在表格内可以管理基于行和列的数据项,表格中的最大 ...

  8. 第15.27节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QTreeWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 树部件(Tree Widget)是Qt Designer中 Item Widgets(It ...

  9. 第15.26节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QListWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 列表部件(List Widget)对应类QListWidget,是从QListView派生 ...

随机推荐

  1. tj

    --统计set @collSql='select sum(case Ca_IssueType when 0 then 1 else 0 end) as IssueCount,sum(case when ...

  2. 遍历id,根据id作为条件循环查询。

    string id = "OE09924008161405102,OE36765709071405102,OE87852044171405102,OE09924008161405102&qu ...

  3. 关于XML(一)。

    关于XML 什么是XML? XML是可扩展标记语言.类似于HTML,XML的宗旨是旨在传输数据,而非显示数据.其标签没有预定义,您需要自行定义标签.XML具有自我描述性,是W3C的推荐标准. XML与 ...

  4. Android Listview with different layout for each row

    http://stackoverflow.com/questions/4777272/android-listview-with-different-layout-for-each-row 其关键在重 ...

  5. oracle - 创建数据库

    在服务器端的oracle,用户有点差异,当 我通过 'sqlplus / as sysdba' 命令登陆后,并不能创建数据库,sysdba拥有最高的系统权限,登陆后是 sys,以as sysdba登录 ...

  6. Simple screenshot that explains the singleton invocation.

    Here is the code: /* Some class,such as a config file,need to be only one.So we need to control the ...

  7. index full scan/index fast full scan/index range scan

    **************************1************************************* 索引状态:          valid.      N/A .    ...

  8. helloServlet

    创建第一个web程序 用myeclipse创建一个web项目,继而创建一个servlet 自动帮助你创建了一个web.xml文件 <servlet>....<servlet>指 ...

  9. python 数据结构

    Python的数据结构主要分为set(),list[],和dict{}.这篇文章主要记载这几种结果在使用过程中的一些技巧或其中一些函数的用法区别. 1.函数get()与setdefault()区别: ...

  10. wordpress通过代码禁用IE8, IE9,IE10等IE浏览器兼容视图模式(Compatibility View)

    目前wordpress主流主题大多都放弃了对IE6的支持!甚至IE6,IE7,IE8等的兼容模式也不支持!目前特别是国内的双核浏览器大多数使用使用IE内核都是使用的兼容模式!那将是非常糟糕!如何让IE ...