QThread与多线程(比较清楚)
QThread类为我们提供了一种平台无关的管理线程的方式。一个QThread对象管理应用程序中的一个线程,该线程从run()函数开始执行。并且,默认情况下,我们可以在run()函数中通过调用QThread::exec()函数来在当前线程中开启一个事件循环。
而使用QThread开启线程的最常用的方式 就是继承QThread类,重写其run()方法,因为我们刚才就说过,QThread代表的线程就是从run()函数开始运行的。
例如:
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
在这个例子中,该线程会在run()函数返回后退出。又因为我们没有在run()函数中调用exec(),所以该线程中没有运行事件循环。
另一种使用线程的方法,是将要完成的工作封装到一个工作者对象中,然后使用QObject::moveToThread()函数将该对象移动到一个线程对象中。如下:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
在这个例子中,Worker的槽函数会在一个独立的线程中运行。但是,我们可以自由的将Worker的槽函数连接到任意的信号上,任意的对象上,任意的线程中。并且,由于Qt提供的信号和槽连接类型中的queued connections类型,使我们可以安全的跨线程连接信号和槽。
并且,很重要的一点是,QThread对象是存活在创建它的那个线程中,而不是在运行run()函数的新线程中。这意味着,连接到QThread的所有的排队型槽函数都是在旧线程中执行的。因此,如果我们希望我们调用的槽函数也在新线程中执行,就必须使用这种工作对象的方式。新的槽函数 不应该直接实现在QThread的子类中。
还有,当子类化QThread时,要记住的一点是线程对象的构造函数在旧线程中运行,而run()在新线程中运行。所以,如果在这两个函数中都访问了一个成员变量,那么就是在两个不同的线程中访问的,要确保访问的安全性。
下面,我们分别使用这两种方式,通过打印线程id的方法,来看一下它们的区别。
我们先写一个工作者类,继承自QObject:
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QThread>
#include <QDebug>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
public slots:
void start();
};
#endif // WORKER_H
#include "worker.h"
Worker::Worker(QObject *parent) : QObject(parent)
{
}
void Worker::start()
{
qDebug() << "child thread: " << QThread::currentThreadId();
}
我在该类中,只定义了一个start()函数,作为我们线程的执行体,其所做的工作也很简单,只是打印出本线程的id。
接下来,再写一个Controller类,其也继承自QObject,用来控制线程的启动。
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include "worker.h"
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(QObject *parent = 0);
void start();
signals:
void operate();
private:
QThread workerThread;
};
#endif // CONTROLLER_H
#include "controller.h"
Controller::Controller(QObject *parent) : QObject(parent)
{
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::start);
workerThread.start();
}
void Controller::start()
{
emit operate();
}
在这个类中,我们在声明了一个start()函数和一个operate()信号,还有一个QThread对象。然后在构造函数中,实例化一个Worker类对象,再使用moveToThread()函数,将其移动到我们定义的线程对象中;最后,为了启动我们的执行体,我们将operate()信号连接到Worker的start()槽函数上。然后,启动我们的线程对象。
main函数如下:
#include <QCoreApplication>
#include "controller.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread: " << QThread::currentThreadId();
Controller controller;
controller.start();
return a.exec();
}
我们定义一个Controller的对象,然后调用其start()方法即可。而根据我们在Controller::start()的实现中,发出了operate()信号,该信号又连接到Worker::start(),所以会触发该函数的执行,即我们的线程执行体。
其执行结果如下:
可以看出,子线程的槽函数确实在一个独立的线程中运行。
而如果我们用继承QThread的方法来实现该功能,则会看到不同的结果。
先实现Thread类如下:
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include <QDebug>
class Thread : public QThread
{
Q_OBJECT
public:
Thread(QObject *parent = Q_NULLPTR);
public slots:
void Come();
protected:
void run() Q_DECL_OVERRIDE;
};
#endif // THREAD_H
#include "thread.h"
Thread::Thread(QObject *parent)
:QThread(parent)
{
}
void Thread::Come()
{
qDebug() << "child thread: " << QThread::currentThreadId();
}
void Thread::run()
{
exec();
}
我们继承了QThread类,重新了run()方法,这是继承QThread开启线程所必须的一步。另外,我们还定义了一个槽函数,Come(),在该函数中打印了本线程的id。为了让线程不退出,我们在run()函数中调用了QThread::exec()函数,为该线程开启一个事件循环,等待某个信号来触发Come()槽函数。
接下来,再修改Controller类如下:
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include "worker.h"
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(QObject *parent = 0);
void start();
signals:
void operate();
};
#endif // CONTROLLER_H
#include "controller.h"
#include "thread.h"
Controller::Controller(QObject *parent) : QObject(parent)
{
}
void Controller::start()
{
emit operate();
}
我们只在start()函数中,发送了operate()信号。
再修改main函数如下:
#include <QCoreApplication>
#include "controller.h"
#include "thread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread: " << QThread::currentThreadId();
Controller controller;
Thread thread;
QObject::connect(&controller, SIGNAL(operate()), &thread, SLOT(Come()));
thread.start();
controller.start();
return a.exec();
}
我们先定义了Controller和Thread对象,然后将Controller的operate()信号连接到Thread的Come()槽函数上,紧接着启动线程,等待信号。最后调用Controller的start()方法,发送我们定义的信号,该信号又会触发Thread类的槽函数,打印出线程ID。
该种方式的执行结果如下:
可见,线程类Thread中的槽函数并没有运行在run()函数所在的新线程中,而是和main函数在同一个线程中,即创建线程对象的线程。这有时恐怕不是我们想要的。所以,当需要线程中的槽函数完全在另一个新线程中执行时,就需要使用moveToThread()的方法。
---------------------
作者:求道玉
来源:CSDN
原文:https://blog.csdn.net/Amnes1a/article/details/70171519
版权声明:本文为博主原创文章,转载请附上博文链接!
QThread与多线程(比较清楚)的更多相关文章
- [技术博客]采用Qthread实现多线程连接等待
采用Qthread实现多线程连接等待 本组的安卓自动化测试软件中,在测试开始前需要进行连接设备的操作,如下图左侧的按钮 后端MonkeyRunner相关操作的程序中提供了connect() ...
- 在Qt(C++)中使用QThread实现多线程
1. 引言 多线程对于需要处理耗时任务的应用很有用,一方面响应用户操作.更新界面显示,另一方面在"后台"进行耗时操作,比如大量运算.复制大文件.网络传输等. 使用Qt框架开发应用程 ...
- 【QT】子类化QThread实现多线程
<QThread源码浅析> 子类化QThread来实现多线程, QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里.正确启动线程的方法是调用QThrea ...
- QThread创建多线程程序
最近在阅读Qt 5.9 C++开发指南,为了加深对书本上内容的理解,参照书上的讲解尝试写了一些demo,用于以后工作中查阅,如果涉及侵权请告知,实例程序samp13_1 mythread.h #ifn ...
- [ PyQt入门教程 ] PyQt5中多线程模块QThread使用方法
本文主要讲解使用多线程模块QThread解决PyQt界面程序唉执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题.用户使用工具过程中出现这些问题时会误以为程序出错,从而把程序关闭.这样 ...
- QT多线程及通过事件进行通信(通过自定义事件,然后QApplication::postEvent给主界面,我之前用的是信号槽)
可以通过QThread实现跨平台的多线程开发,Qt库负责在特定平台上的特定多线程实现.要采用QThread进行多线程开发,首先需要包含头文件: #include <QThread> 然后需 ...
- Qt多线程-QtConcurrent并行运算高级API
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt多线程-QtConcurrent并行运算高级API 本文地址:http://tec ...
- PyQt5中Web操作与多线程定时器
1.装载外部网页页面'''用web浏览器控件QWebEngineView控件显示网页PyQt5和Web的交互技术 同时使用python和Web开发程序,混合开发 Python+JavaScript+H ...
- 【QT】子类化QObject+moveToThread实现多线程
往期链接: <QThread源码浅析> <子类化QThread实现多线程> 从往期<QThread源码浅析>可知,在Qt4.4之前,run 是纯虚函数,必须子类化Q ...
随机推荐
- C#运用反射调用其他程序集中的代码
加载程序集 AssMedicalAdvice = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, "Inscript ...
- WPF入门教程系列三
WPF之Binding的使用(一) 一. 前言 初学WPF经常被Binding搞得苦不堪言,Binding的重用性就不做介绍了,在WPF应用程序开发中Binding是一个非常重要的部分.WPF也是近 ...
- C#中ArrayList类的使用
ArrayList类 使用大小可按须要动态添加的数组实现IList接口 命名空间:System.Collections 程序集:mscorlib 语法: public class ArrayList: ...
- Silverlight实例教程 - Validation数据验证基础属性和事件(转载)
Silverlight 4 Validation验证实例系列 Silverlight实例教程 - Validation数据验证开篇 Silverlight实例教程 - Validation数据验证基础 ...
- tuple 元组
创建单元素tuple tuple和list一样,可以包含 0 个.1个和任意多个元素. 包含多个元素的 tuple,前面我们已经创建过了. 包含 0 个元素的 tuple,也就是空tuple,直接用 ...
- linux设备号分配
参考:http://blog.chinaunix.net/uid-24460251-id-2606762.htmlhttp://blog.csdn.net/zjjyliuweijie/article/ ...
- JIRA /mnt/server/atlassian-jira-6.3.6-standalone/bin/start-jira.sh
JIRA 敏捷开发平台部署记录 分类: 敏捷开发 1.1 jira说明 JIRA是Atlassian公司出品的项目与事务跟踪工具,被广泛应用于缺陷跟踪.客户服务.需求收集.流程审批.任务跟踪. ...
- pom.xml settings.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Soft ...
- mysql 1005 错误
建立外键的时候两个 表的相对应的 类型不一致!
- [浪风推荐]javascritp中倒计定时器和循环定时器
在javascritp中,有两个关于定时器的专用函数,分别为: 1.倒计定时器:timename=setTimeout(“function();”,delaytime); 2.循环定时器:timena ...