原文链接:http://hi.baidu.com/cyclone/blog/item/65f3f603294f2e783812bb51.html

注意:请优先考虑Qt 线程基础(QThread、QtConcurrent等)

dbzhang800 2011.06.18

本文主要内容:

在任务一中,用 四 种方式实现:点击界面按钮,开线程运行一段程序,结果显示在一个Label上。
1. 用不正确的方式得到看似正确的结果
2. 用Qt Manual 和 例子中使用的方法
3. 用一种好用但被Qt开发人员批判的方法
4. 用一种被开发人员强烈推荐,但Qt Manual和例子中只字未提的方法

  • 为了简单起见,本文只讲如何做及其结果是什么,而不讲其原因是什么(估计大家对原因也不会感兴趣,详见: QThread 使用探讨  和 QThread使用方法)。

  • 本文只考虑两个线程(即主线程和一个次线程)的情况。

QWidget

  • QWidget及其派生类均 不能在次线程中使用或创建

Manual 中的原话:

  • The GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
  • 因为不允许,所以尝试这么做的,几乎很快都能回头。毕竟signals和slots用起来确实蛮方便
  • 但是,回头后,就理解和用对 QThread 了么?

QThread

概念一:QThread 对象本身所依附的线程 和它管理的线程不是同一个线程。

  • 前者是主线程
  • 后者是次线程

概念二:你在QThread派生类中定义的槽在主线程而不是在次线程中执行的。

  • run 函数是线程的入口点,run内的代码才是在次线程中运行的代码

概念三:除了Manual和Qt例子中给出的用法外,QThread有一种更容易且被推荐的使用方法:

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

关于本文的例子

  • 为了代码简单,所有例子都是单一的源文件,保存为 main.cpp

    • 你从代码中包含的 #include“main.moc”应该能看出

  • 为了省几行代码,头文件都是直接包含 QtCore 和 QtGui。

  • 为了清楚告诉大家槽函数分别是在那个线程运行的,调用了几处 currentThreadId 函数
    • main 函数中输出主线程IDqDebug()<<"main: "<<QThread::currentThreadId();

    • run 函数中输出次线程IDqDebug()<<"thread: "<<currentThreadId();

    • 槽函数中输出其在哪个线程中执行qDebug()<<"slots1: "<<currentThreadId();

  • 因为用了qDebug,所以你的pro文件内最好加上 CONFIG+=console

  • 同样为了省代码,例子中未考虑线程如何正常结束的问题。

任务一

点击界面按钮,开线程运行一段程序,结果显示在一个Label上。

  • 定义一个Widget,上面放置 QPushButton 和 QLabel
  • 定义一个Thread,执行我们的代码,然后通知 Widget

第一次尝试

很容易想到方法,代码可以工作,结果正确。但 ... 未必和你想得一样

  • Thread 中定义一个slot1函数,接受数据,计算其立方,然后将结果通过信号发出
  • Widget 中按钮每点击一次,发出的数据加1,用label接受Thread的信号
#include <QtCore>
#include <QtGui> class Thread:public QThread
{ Q_OBJECT
public: Thread(){}
public slots: void slot1(int v) { qDebug()<<"slots1: "<<currentThreadId(); emit sig1(QString::number(v*v*v)); }
signals: void sig1(const QString& t);
protected: void run() { qDebug()<<"thread: "<<currentThreadId(); exec(); }
}; class Widget:public QWidget
{ Q_OBJECT
public: Widget():m_label(new QLabel), m_button(new QPushButton("Button")), m_thread(new Thread) { QVBoxLayout * vbox = new QVBoxLayout(this); vbox->addWidget(m_label); vbox->addWidget(m_button); setLayout(vbox); connect(m_button,SIGNAL(clicked()),this,SLOT(onButtonClicked())); connect(this,SIGNAL(clicked(int)),m_thread,SLOT(slot1(int))); connect(m_thread,SIGNAL(sig1(QString)),m_label,SLOT(setText(QString))); m_thread->start(); }
signals: void clicked(int v);
private slots: void onButtonClicked() { static int v = 0; emit clicked(v); v++; }
private: QLabel * m_label; QPushButton * m_button; Thread * m_thread;
}; #include "main.moc"
int main(int argc, char** argv)
{ QApplication app(argc, argv); qDebug()<<"main: "<<QThread::currentThreadId(); Widget w; w.show(); return app.exec();
}

一切工作正常,但看看控制台输出呢?

main:  3055777552
thread:  3024481136
slots1:  3055777552
slots1:  3055777552
slots1:  3055777552
...

这儿明确告诉你,slot1 是在主线程中执行的。

尝试二

我们试试 Qt Manual和 Qt 例子中采用的解决方案。

槽函数不是在主线程运行么,而run函数不是次线程么?那么我们就:

  • 在槽函数中做个标记
  • 在run函数中根据标记进行运行

这样以来,尽管槽函数在仍在主线程,但费时的计算代码都在次线程了。

对Thread的类的改造如下(程序其他部分和 尝试一 完全一样):

class Thread:public QThread
{ Q_OBJECT public: Thread(){}
public slots: void slot1(int v) { qDebug()<<"slots1: "<<currentThreadId(); m_mutex.lock(); m_vals.enqueue(v); m_mutex.unlock(); }
signals: void sig1(const QString& t);
protected: void run() { qDebug()<<"thread: "<<currentThreadId(); while(1) { m_mutex.lock(); if (!m_vals.isEmpty()){ int v = m_vals.dequeue(); emit sig1(QString::number(v*v*v)); } m_mutex.unlock(); } }
private: QQueue<int> m_vals; QMutex m_mutex;
};

注意哦,因为 slot 函数在主线程中,而run函数在次线程中,所以二者需要 QMutex 实现对变量的安全访问。如果你认真看过Qt自带的例子,会发现它始终强调 QMutex 的使用。

尝试三

尝试二是"正统"的做法,但如过你用Google搜索过。那么你可能不会选择尝试二,而是会使用下面的方法(其他部分和尝试一 完全一样)

class Thread:public QThread
{ Q_OBJECT public: Thread(){ moveToThread(this); }
...

这样以来,slot函数确实是在次线程工作的,看看控制台输出

main:  3056785168
thread:  3024444272
slots1:  3024444272
slots1:  3024444272
...

很有意思?不是么,一条 moveToThread(this),移动到自己。然后问题解决了。

  • 因为前面说了,QThread 所依附线程 和 它管理的线程不是同一个。
  • 这样,其实将自己所依附的线程改为自己所管理的线程了。

o(∩∩)o...哈哈,不要太高兴哦,这个方法看起来比较舒服,但是它是被官方人员强烈批判的用法

尝试四

终于到我想写的代码了,这是Qt线程的开发者建议的使用方式,但很可惜。直到目前(Qt4.7.0),手册和例子中对此都只字为提。

  • 我们不子类话QThread了,我们只需要子类话一个QObject,然后将其move到QThread就行了,看代码:
  • 不用子类化QThrad了,我们只需要子类话一个 QObject,需要在次线程中工作的代码,直接放到它的槽中
class Worker:public QObject
{ Q_OBJECT
public: Worker(){}
public slots: void slot1(int v) { qDebug()<<"slots1: "<<QThread::currentThreadId(); emit sig1(QString::number(v*v*v)); }
signals: void sig1(const QString& t);
};
  • 因为没有Thread类了,只有Worker类,Widget代码需做点改动
class Widget:public QWidget
{ Q_OBJECT public: Widget():m_label(new QLabel), m_button(new QPushButton("Button")), m_worker(new Worker) { QVBoxLayout * vbox = new QVBoxLayout(this); vbox->addWidget(m_label); vbox->addWidget(m_button); setLayout(vbox); QThread * thread = new QThread(this); m_worker->moveToThread(thread); connect(m_button,SIGNAL(clicked()),this,SLOT(onButtonClicked())); connect(this,SIGNAL(clicked(int)),m_worker,SLOT(slot1(int))); connect(m_worker,SIGNAL(sig1(QString)),m_label,SLOT(setText(QString))); thread->start(); }
signals: void clicked(int v);
private slots: void onButtonClicked() { static int v = 0; emit clicked(v); v++; }
private: QLabel * m_label; QPushButton * m_button; Worker * m_worker;
};

main 函数还是和尝试一完全一样

控制台输出结果如下

main:  3056961296
slots1:  3024616304
slots1:  3024616304
....

一共两个线程,且二者id不同,说明slot在次线程中

恩。这篇文字似乎又不短了,看来任务二要另起一篇了。

  • -- dbzhang800 于 20101023

QThread与QWidget使用相关推荐

  1. 树莓派3B Qt 多线程测温测距(12)

    目录 内容 界面 大致结构 使用多线程的原因 一个简单多线程样例 1.新建一个qt工程: 2.添加 一个类thread_one(名字任意): 3.在ui里添加一个LCD Number和一个button ...

  2. Python Qt GUI设计:QTimer计时器类、QThread多线程类和事件处理类(基础篇—8)

    目录 1.QTimer计时器类 2.QThread多线程类 3.事件处理类 一般情况下,应用程序都是单线程运行的,但是对于GUI程序来说,单线程有时候满足不了需求.例如,如果需要执行一个特别耗时的操作 ...

  3. pyqt5 使用 QTimer, QThread, pyqtSignal 实现自动执行,多线程,自定义信号触发。

    渣渣用法,请等待我心情好的时候更新. 1.第一个例子 1.1 先看mainwindow.py from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Ma ...

  4. 在Qt(C++)中使用QThread实现多线程

    1. 引言 多线程对于需要处理耗时任务的应用很有用,一方面响应用户操作.更新界面显示,另一方面在"后台"进行耗时操作,比如大量运算.复制大文件.网络传输等. 使用Qt框架开发应用程 ...

  5. PYQT中QThread输出到textBrowser

    问题概述 在PYQT中,有时我们会需要将log信息等实时打印到ui控件上,但是由于PYQT的进程保护机制,我们无法像使用print函数中那样,直接使用就能打印出来信息.所以通常都会发现,PYQT中的U ...

  6. Qt修炼手册11_多线程编程和QThread类

    1.事件循环 学习QT多线程编程之前,有必要先熟悉事件循环的概念. 先看一个单线程界面程序的主函数代码: int main(int argc, char* argv[]) {QApplication ...

  7. Qt: QTimer和QThread

    让QTimer 跑在其他线程. 一般写法如下. 1. 在main thread中为worker thread指定定时器. QThread* thread = new QThread(this);thr ...

  8. QT之深入理解QThread

    QT之深入理解QThread 理解QThread之前需要了解下QThread类,QThread拥有的资源如下(摘录于QT 5.1 帮助文档): 在以上资源中,本文重点关注槽:start():信号:st ...

  9. Qt文档阅读笔记-Qt工作笔记-QThread解析与实例(主线程发送信号给子线程)

    目录 QThread 官方解析 博主栗子 子线程发射信号给主线程 QThread 官方解析 Detailed Description QThread类提供了跨平台的线程管理的API. QThread对 ...

最新文章

  1. [Python3网络爬虫开发实战] 1.7.1-Charles的安装
  2. php定界符EOF讲解
  3. 服务器保存所有用户的操作指令(history)
  4. 3.1.3 操作系统覆盖技术与交换技术的思想
  5. mysql 之jdbc idea版
  6. HTMLTestRunner.py内容
  7. python自带的解释器叫做_21条python面试题,值得收藏!
  8. hdu 1495非常可乐 BFS
  9. 下载devc++和codeblocks记录
  10. 2021 年最佳开源软件榜单出炉!
  11. Iptables 中文 man 文档
  12. 经典三层模型制作学校论坛系统(BBS)
  13. 计算机程序c语言教材,C语言程序设计(高等学校计算机基础教育教材精选)
  14. python 递归函数 - 刘江的python教程_python 函数 - 刘江的python教程
  15. ES6 数组函数forEach()、map()、filter()、find()、every()、some()、reduce()
  16. Oracle DB audit
  17. 细说集线器、交换机和路由器
  18. 公众号开发素材管理,php删除微信素材
  19. LintCode(M) 乱序字符串
  20. 记一个脚本解释器的开发

热门文章

  1. .Net中委托的协变和逆变详解
  2. Exp 8 Web基础
  3. Java_基础—用LinkedList模拟栈数据结构的集合并测试
  4. 【Java】在eclipse中使用maven进行项目构建 入门篇
  5. HoloLens开发手记 - 使用混合现实捕捉 Using mixed reality capture
  6. Patrick Hughes - 错觉3D雕塑艺术
  7. 使用FileDialog查看文件内容
  8. 【Java】什么是多态?多态的实现机制是什么?
  9. 接口测试学习之json
  10. Servlet Cookie 处理