[QT编程系列-25]:多线程机制 - QThread和MoveToThread简介

2023-11-19

目录

第1章 简介

1.1 多线程的目的

1.2 QThread多线程使用方法

1.3 QT支持多线的步骤

第2章 QThread

2.1 概述

2.2 moveToThread


第1章 简介

1.1 多线程的目的

QThread类提供了一个与平台无关的管理线程的方法。

在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,比如大量运算,复制大文件,网络传输等。

QT(也称为Qt框架)是一个用于开发跨平台应用程序的C++库。它提供了丰富的功能,包括多线程支持。

多线程是一种在同一时间处理多个任务的技术,它可以改善应用程序的响应性和性能

1.2 QThread多线程使用方法

1.  自定义Thread继承QThread类

2. QObject::moveToThread()

第2章 QThread

2.1 QThread支持多线程的步骤

在QT中使用多线程可以通过以下几个步骤来实现:

  1. 引入Qt多线程相关的类:在你的代码中引入/包含以下类的头文件:QThreadQRunnable、QThreadPool、QObject等。

  2. 创建一个继承自QThread或QRunnable的类:这个类将被用来执行需要在后台线程中运行的任务。如果你需要更多的灵活性和控制,可以选择继承自QThread,否则继承自QRunnable会更简单。

  3. 重写run()方法:在你的自定义类中重写run()方法,并在其中实现你的任务逻辑。

  4. 创建并启动线程:在主线程中创建你的自定义类的实例,然后调用start()方法来启动线程。这将自动调用你的run()方法。

以下是一个简单的示例代码,展示了如何在QT中使用多线程:

#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
    Q_OBJECT
public:
    void run() override
    {
        // 执行你的任务逻辑
        qDebug() << "Hello from thread";
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyThread thread;
    thread.start();

    // 主线程继续执行其他任务
    qDebug() << "Hello from main thread";

    return app.exec();
}

在上面的例子中,我们创建了一个名为MyThread的类,继承自QThread。我们重写了run()方法,其中打印了一条消息。在main()函数中,我们创建了一个MyThread实例,并调用它的start()方法来启动线程。同时,主线程也继续执行其他任务。当运行这个程序时,你会看到两条消息,一条来自主线程,一条来自子线程。

需要注意的是,当使用多线程编程时,需要小心处理线程间的共享数据访问,避免出现竞争条件和其他线程相关的问题。QT提供了一些同步工具,如互斥锁和信号量,来帮助你实现线程间的安全数据访问。

Qt提供了强大的多线程支持,使开发者能够轻松地在应用程序中实现并发和并行处理。

以下是Qt多线程支持的一些重要组件和功能:

  1. QThread类:QThread是Qt提供的多线程编程的基础类。开发者可以通过继承QThread类来创建自己的线程类,并实现run()函数来定义线程的执行逻辑。

  2. 信号和槽机制:Qt的信号和槽机制是一个强大的线程间通信机制。线程间的对象通信可以通过信号和槽的方式来实现,无需直接操作共享数据。这种机制可以有效地降低多线程编程的复杂性。

  3. QMutex类:QMutex是一个互斥锁类,用于保护共享数据的访问。通过QMutex,你可以确保在同一时间只有一个线程可以访问被保护的代码区域,从而避免竞态条件。

  4. QWaitCondition类:QWaitCondition是一个条件变量类,用于线程间的等待和唤醒操作。通过QWaitCondition,你可以实现线程的等待直到某个条件满足,并在条件满足时唤醒线程。

  5. QThreadPool类:QThreadPool是负责管理和调度线程的线程池类。通过QThreadPool,你可以将任务提交到池中,线程池会自动为你管理线程的生命周期和任务的执行。

除了上述的核心组件,Qt还提供了其他一些辅助类和工具,如QReadWriteLock(读写锁)、QSemaphore(信号量)、 QtConcurrent(并行编程框架)等,这些类和工具都可以帮助你更好地处理多线程编程。

在使用Qt多线程时,需要注意以下几点:

  1. 避免对共享数据的直接访问:尽量不要直接操作共享数据,而是通过信号和槽或使用互斥锁等机制来确保线程安全。

  2. 避免长时间阻塞主线程:如果在主线程中有耗时的操作,应该将其放入后台线程以避免阻塞主线程,保证应用程序的响应性。

  3. 小心使用线程间传递的数据:在线程间传递数据时需要注意数据的有效性和生命周期,避免出现悬空指针或无效引用等问题。

Qt的多线程支持可以帮助你更轻松地编写多线程应用程序,并在并发和并行处理方面提供了很大的便利性。通过合理地使用Qt的多线程功能,你可以提高应用程序的性能和响应性,并更好地利用多核处理器的计算能力。

2.2 QThread讲解

QThread是Qt提供的一个类,用于在应用程序中创建和管理线程。

它封装了底层的线程操作,使多线程编程变得更加方便和可管理。

以下是一些使用QThread的一般步骤和常见用法:

  1. 创建一个自定义的QThread子类,重写其run()函数。run()函数是在线程启动时执行的入口点,你可以在其中编写你的线程逻辑。
class MyThread : public QThread {
public:
    void run() override {
        // 编写线程逻辑
    }
};
  1. 创建MyThread类的实例,并调用start()函数来启动线程。
MyThread* thread = new MyThread();
thread->start();

  1. 可选地,连接信号和槽以进行线程间通信。你可以使用Qt的信号和槽机制来在主线程和子线程之间进行通信。
connect(someObject, &SomeObject::someSignal, thread, &MyThread::someSlot);
// 或者反向连接
connect(thread, &MyThread::someSignal, someObject, &SomeObject::someSlot);

  1. 在需要的时候,可以调用wait()函数来等待子线程的结束。也可以调用quit()或terminate()函数来停止线程。
thread->wait();      // 等待线程结束
thread->quit();      // 请求线程退出
thread->terminate(); // 强制终止线程

  1. 检查线程的状态和控制线程行为。你可以使用QThread的函数来获取线程的当前状态,例如isRunning()、isFinished()、isInterruptionRequested()等。
if (thread->isRunning()) {
    // 线程正在运行
}

QThread还提供了其他一些辅助功能,例如设置线程的优先级、设置线程栈的大小、事件循环和事件处理等。

需要注意的是,直接继承QThread并重写run()函数是一种使用QThread的传统方式。但是从Qt 5.2版本开始,Qt提供了一种更简单和更推荐的方式来处理多线程,即使用Qt的信号和槽机制与QRunnable和QThreadPool或Qt Concurrent组合使用。

希望这些信息对使用QThread类来创建和管理线程有所帮助。如果有任何进一步的问题,请随时提问。

2.3 QThread成员函数

QThread类是Qt中用于创建和管理线程的类,它提供了许多成员函数来管理线程的行为和状态。

以下是QThread类中一些常见的成员函数:

  1. start():启动线程,使其以独立线程的方式开始执行run()函数中的代码。

  2. run():需要在子类中重新实现的虚函数,定义线程执行的主要代码逻辑。

  3. wait():阻塞当前线程,直到线程执行完成。

  4. sleep():使当前线程睡眠指定的毫秒数。

  5. terminate():终止线程的执行。这是一种比较粗暴的方法,通常不推荐使用。

  6. quit():停止线程的事件循环,推出线程执行。通常与event loop(事件循环)一起使用。

  7. msleep():使当前线程睡眠指定的毫秒数。

  8. usleep():使当前线程睡眠指定的微秒数。

  9. isRunning():判断线程是否正在执行。

  10. currentThreadId():返回当前线程的唯一标识符。

  11. priority() / setPriority():获取或设置线程的优先级。

  12. finished():当线程执行完成时发出的信号。

  13. started():当线程开始执行时发出的信号。

  14. finished():在线程执行完成后,由QThread对象发出的信号。

  15. moveToThread(QThread* thread):将对象移动到指定的线程中运行。

这只是QThread类中的一些常见成员函数,通过使用这些函数,可以管理线程的执行、状态和行为。根据具体的应用需求,可能会使用更多的QThread成员函数来完成线程的管理和操作。

第3章  moveToThread

3.1 概述

moveToThread()是一个Qt中的成员函数,用于将对象移到指定的线程中执行。

它允许您在多线程应用程序中管理对象的线程执行上下文

以下是moveToThread()函数的语法和用法:

void QObject::moveToThread(QThread* thread)
  • thread:指向目标线程的指针。

moveToThread()函数的作用是将调用该函数的对象与指定的线程相关联。

这意味着对象的方法和事件将在目标线程的上下文中执行,而不是在调用线程(创建对象的线程)的上下文中执行。

以下是一个使用moveToThread()函数的示例:

#include <QObject>
#include <QDebug>
#include <QThread>

class Worker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {
        qDebug() << "Worker thread:" << QThread::currentThread();
        // 执行耗时操作
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    qDebug() << "Main thread:" << QThread::currentThread();


    // 创建一个新的管理线程上下文的对象
    QThread* workerThread = new QThread;

    // 创建一个可以task对象,该对象可以执行某种操作,比如while循环的函数
    Worker* worker = new Worker;

    // 将Worker对象移到 workerThread 线程
    // 如果没有这行代码,则worker的task在创建worker线程中执行,而不是目标线程上下文中执行
    worker->moveToThread(workerThread);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数

    // 线程QThread也是类的对象,因此,可以通过槽函数和信号在对象间通信:
    // 在QThread对象中执行另一个需要循环执行的对象的成员函数(while循环)
    QObject::connect(workerThread, &QThread::started, worker, &Worker::doWork);

    //在QThread对象的上下文中发送一个started信号,在QThread线程对象的上下文中执行worker对象中的槽函数,该槽函数是一个while循环执行的函数。
    workerThread->start();

    return app.exec();
}

在上面的示例中,我们创建了一个Worker类,该类继承自QObjectWorker类中定义了一个doWork()方法用于执行耗时的工作。

在主函数中,我们创建了一个worker对象和一个workerThread线程对象。然后,使用moveToThread()worker对象移动到workerThread线程中。这样,worker对象的doWork()方法将在workerThread线程中执行。

通过连接workerThreadstarted信号与workerdoWork()槽函数,当线程启动时,doWork()方法将自动被调用。

需要注意的是,moveToThread()函数只能在对象所属的线程中调用,否则会导致未定义的行为。因此,建议在创建对象后尽早调用moveToThread()函数来确保对象在预期的线程中执行。

moveToThread()函数在多线程编程中非常有用,它允许您将对象移动到适当的线程,并在该线程中执行相关的方法和事件处理。这有助于将耗时的操作与UI交互分开,保持应用程序的响应性。

第4章  QThread与moveToThread创建线程的比较

4.1 代码实例

#include "mainwindow.h"

#include <QApplication>
#include <QThread>
#include <QDebug>

#include <QObject>
#include <QWidget>



class MyThread : public QThread {
public:
    void run() override {
        qDebug() << "Run Thread started";

        // 在这里执行耗时任务
        int i = 0;
        while(1)
        {
            sleep(3);
            qDebug() << i << ": run thread:" << QThread::currentThread();
            i++;
        }

        qDebug() << "Run Thread finished";
    }
};



class MyWorker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {

        qDebug() << "doWork Thread started";

        while(1){
            qDebug() << "Worker thread:" << QThread::currentThread();
            // 执行耗时操作
            QThread::sleep(3);
        }

        qDebug() << "doWork Thread finished";
    }
};



int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    qDebug() << "Main thread started: " << QThread::currentThread() ;

    // 创建并启动run线程1
    MyThread thread_run1;
    thread_run1.start();

    // 创建并启动run线程2
    MyThread thread_run2;
    thread_run2.start();



    //创建一个管理线程上下文的对象
    QThread* workerThread1 = new QThread;

    //创建一个task对象
    MyWorker* myworker1 = new MyWorker;

    // 将 Worker 对象移到 workerThread 线程
    //myworker1->moveToThread(workerThread1);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数
    // 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
    QObject::connect(workerThread1, &QThread::started, myworker1, &MyWorker::doWork);

    workerThread1->start();


    //创建一个管理线程上下文的对象
    QThread* workerThread2 = new QThread;

    //创建一个task对象
    MyWorker* myworker2 = new MyWorker;

    // 将 Worker 对象移到 workerThread 线程
    myworker2->moveToThread(workerThread2);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数
    // 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
    QObject::connect(workerThread2, &QThread::started, myworker2, &MyWorker::doWork);

    workerThread2->start();

    qDebug() << "Main thread finished" << QThread::currentThread() ;

    w.show();
    return a.exec();
}

4.2 输出结果

Main thread started: QThread(0x9c7770)

Main thread finished QThread(0x9c7770)

Run Thread started

Run Thread started

doWork Thread started

Worker thread: QThread(0x30593d0)

doWork Thread started

Worker thread: QThread(0x9c7770 //没有myworker1->moveToThread(workerThread1),因此,在主线程上下文中执行,因此,执行比较缓慢,滞后于workerThread2上下文的myworker2

0 : run thread: QThread(0x8bfc30)

0 : run thread: QThread(0x8bfc20)

Worker thread: QThread(0x30593d0)

Worker thread: QThread(0x9c7770)

1 : run thread: QThread(0x8bfc30)

1 : run thread: QThread(0x8bfc20)

Worker thread: QThread(0x30593d0)

Worker thread: QThread(0x9c7770)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[QT编程系列-25]:多线程机制 - QThread和MoveToThread简介 的相关文章

  • 如何使用 ASP.NET MVC 进行 HTTP 调用?

    我正在尝试做的事情 我试图练习进行 HTTP 调用 如果这就是它的名字 来自一个简单的 ASP NET MVC Web 应用程序 为此 我尝试从以下位置获取天气详细信息打开天气地图 http openweathermap org appid
  • 中间件 API 的最佳实践是什么? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我们正在开发一个中间件 SDK 采用 C 和 Java 语言 供游戏开发人员 动画软件开发人员 阿凡达开
  • 同步执行异步函数

    我对此主题进行了大量搜索 并且阅读了本网站上有关此主题的大部分帖子 但是我仍然感到困惑 我需要一个直接的答案 这是我的情况 我有一个已建立的 Winform 应用程序 但无法使其全部 异步 我现在被迫使用一个全部编写为异步函数的外部库 在我
  • C++ 模板中的名称查找

    我有一些 C 代码 如果没有 fpermissive 选项 就无法再编译 这是我无法分享的专有代码 但我认为我已经能够提取一个简单的测试用例来演示该问题 这是 g 的输出 template eg cpp In instantiation o
  • CMake 和 Visual Studio:如何获得快速、安静的命令行构建?

    我有一个 cmake 项目 它成功地完成了我想要的一切 但我有大约 100 个文件 当我只需要重新编译一个文件时 我厌倦了每次看到生成的巨大输出 每个文件 30 行 明确地说 我正在编译cmake build 得到这个结果 我需要传递给编译
  • C 中的复合语句表达式

    下面的代码不起作用 int i void 999 100 添加括号就可以了 为什么 int i void 999 100 还有另一种方法可以完成此类分配 int i void 999 100 是什么让他们与众不同 在这份声明中 int i
  • 如何在 MFC 中调整对话框大小时移动控件?

    我已经在 MFC 中创建了对话框视图 从下图中可以清楚地看到 如滑块控件和编辑框等 当我调整对话框大小时 这些控件不会移动 在此输入图像描述 https i stack imgur com 7OxAK jpg 我想移动控件以适应对话框 但不
  • 将指针转换为浮点数?

    我有一个unsigned char 通常 这指向一块数据 但在某些情况下 指针就是数据 即 铸造一个int的价值unsigned char 指针 unsigned char intData unsigned char myInteger 反
  • 使用 Selenium for C# 登录 Facebook

    我一直在使用 Selenium C 框架并尝试进行 facebook 登录 但没有任何运气 这是我到目前为止得到的 基于这篇文章 使用 Selenium 测试 Facebook Connect 应用程序 https stackoverflo
  • Entity Framework 4.1 RC:Code First EntityTypeConfiguration 继承问题

    我尝试使用通用的 EntityTypeConfiguration 类来配置所有实体的主键 以便每个派生的配置类不会重复自身 我的所有实体都实现一个公共接口 IEntity 它表示每个实体必须有一个 int 类型的 Id 属性 我的配置基类如
  • async wait 在调用异步方法时返回 Task> 而不是 List

    我正在尝试了解 async wait 的用法 并且研究了一些博客文章 现在我已经编写了一个测试代码 但它没有按照我期望的方式工作 我有一个返回列表的方法 private List
  • C++ 中的 Java ArrayList [重复]

    这个问题在这里已经有答案了 在Java中我可以做 List
  • Qt中如何获取鼠标在屏幕上的位置?

    我想获取屏幕上的鼠标坐标 我怎样才能在 Qt 中做到这一点 在 Windows 上 使用 C 我正在做类似答案中建议的事情对于这个问题 https stackoverflow com q 11737665 1420197 正如文档所述 QC
  • 从窗口内容截取屏幕截图(无边框)

    我正在寻找有关如何使用 C 将表单内容保存在位图中的解决方案 我已经尝试过使用 DrawToBitmap 但它捕获了所有带边框的窗口 这就是这段代码的结果 public static Bitmap TakeDialogScreenshot
  • 枚举器上的 [[maybe_unused]]

    查看规格 maybe unused http en cppreference com w cpp language attributes 它指出 出现在类 typedef 变量 非静态数据成员 函数 枚举或枚举器的声明中 如果编译器对未使用
  • 在不使用 Thread.Sleep c# 的情况下延迟发送电子邮件

    我有一个 for 循环 它循环并每个循环发送一封电子邮件 现在我正在使用 thread sleep 但我希望用户仍然能够与程序交互 只需取消该循环即可 是否可以在不使用 thread sleep 的情况下做到这一点 您是否在 UI 线程上运
  • 为什么 std::ranges::filter_view 对象必须是非常量才能查询其元素?

    include
  • 如何使用 libpq 获取双精度值?

    The examples http www postgresql org docs 9 3 interactive libpq example htmllibpq 文档中展示了如何通过将整数值转换为主机字节序表示来获取整数值 我很好奇必须做
  • 使用 roslyn 扩展 C# 语法

    我试图在没有 else 情况的情况下实现 return if return value if 因为我只想在条件有效时返回或返回一个值 我知道 有if condition return or if condition return value
  • FakeItEasy 代理方法调用实际实现

    我正在尝试将对假对象的调用代理到实际的实现 这样做的原因是我希望能够使用 Machine Specifications 的 WasToldTo 和 WhenToldTo 它们仅适用于接口类型的伪造 因此 我正在执行以下操作来代理对我的真实对

随机推荐

  • js给json对象增加、删除、修改属性

  • JS对象详解(对象创建及属性类型)

    目录 创建对象 属性类型 1 数据属性 2 访问器属性 3 定义多个属性 4 读取属性的特性 对象规则设置 1 冻结 2 密封 3 扩展 对象 ESMAScript把对象定义为 无序属性的集合 其属性可以包含基本值 对象或者函数 简单来说
  • ajax extend,GitHub - YuChenLi923/extend-ajax: an AJAX request library

    extend ajax Features ability to specify request headers ability to get response headers if it isn t jsonp it can cache r
  • 在Vue项目使用kindEditor富文本编译器

    1 第一步 npm install kindeditor 2 第二部 新建一个kindeditor组件
  • Anaconda python windows环境安装及完全排坑教程

    本文篇幅较长 浏览器下搜索关键信息请直接使用ctrl F关键词查找 点击以下内容空降 Anaconda是什么 1 0篇 anaconda初体验 1 Anaconda3的安装 pycharm部分 额外内容 2 0篇 当你使用了anaconda
  • bat批处理for /f命令

    含有 F的for详细说明 含有 F的for有很大的用处 在批处理中使用的最多 用法如下 格式 FOR F options i IN file DO command FOR F options i IN string DO command F
  • python读取.csv文件时报错解决方案(无法打开“parsers.pyx”: 找不到文件)

    版权声明 本文为CSDN博主 Jayden yang 的原创文章 遵循CC 4 0 BY SA版权协议 转载请附上原文出处链接及本声明 原文链接 https blog csdn net qq 41997920 article details
  • Class 06 - 良好的编码习惯(保持代码的可读性)

    Class 06 良好的编码习惯 保持代码的可读性 保持代码的可读性 编码风格 命名的注意事项 文件名 对象名称 语法 运算符 逗号 中括号和小括号 大括号 代码缩进 代码的长度 赋值 注释 调试报错 保持代码的可读性 在编写 R语言 或任
  • SpringBoot2.X配置SSL(https访问)

    一 application yml配置 application yml配置 XXX keystore放到项目根目录下与pom xml同级 server port 443 ssl key store XXX keystore 生成的证书名字
  • Linux 挂载目录

    1 查看当前磁盘的情况 fdisk l root dmp3 fdisk l WARNING fdisk GPT support is currently new and therefore in an experimental phase
  • 3. 决策树原理及数学建模实战

    决策树 文章目录 一 概念 二 ID3 2 1 概念 2 2 算法流程 2 3 信息熵 2 4 特点 三 C4 5 3 1 概念 3 2 信息增益率 3 3 处理连续值 3 4 过拟合问题 3 4 1 提出 3 4 2 剪枝 四 CART
  • sqli-labs靶场Less-7

    备注 虽然从首页进来就知道是dump into outfile 但我还是假设按不知道的流程来一步步尝试 这样才会印象深刻 不然我觉得失去练习的意义了 1 访问首页 Less 7 index php id 1 这里的传参点是id 探测六步 判
  • LDO(低压差线性稳压器)设计电源注意事项(学习笔记)

    1 LDO最大输出电流 按照2 3原则选择 即电路总消耗电流的3 2倍 若电路总消耗电流50 mA 那么LDO的最大输出电流为75mA 2 封装散热以及功耗 功耗 输入电压 输出电压 工作电流 按照1 2原则选择LDO封装 达不到的可以PC
  • 多个js文件调用函数问题

    多个js文件调用函数问题 最近在做一个项目 用的 jquery 和 easyui 有很多常用的函数我就把它们写到了common js里面 然后又写了一link jsp 把常用的css和js文件都写在里面 然后页面直接include 写着写着
  • 蒸馏神经网络(Distill the Knowledge in a Neural Network)

    本文是阅读Hinton 大神在2014年NIPS上一篇论文 蒸馏神经网络的笔记 特此说明 此文读起来很抽象 大篇的论述 鲜有公式和图表 但是鉴于和我的研究方向 神经网络的压缩十分相关 因此决定花气力好好理解一下 1 Introduction
  • vuepress-yarn-nodes-静态网页_个人博客搭建

    nodes官网 https nodejs org en 先下载nodes进行安装 一般nodes会自带包管理器npm 注意npm与nodes的对应关系 除了npm之外还有yarn包管理器 一般会用npm安装这个包 npm install g
  • esp32cam门禁系统简易教程

    esp32cam门禁系统简易教程 人脸识别 1 环境安装 最好有梯子 arduino IDE 1 官网下载地址 选择相应版本下载Windows ZIP file 无脑安装 2 配置IDE 打开IDE 文件 gt 首选项 gt 附加开发板管理
  • Android属性动画

    http bbs itheima com thread 172632 1 1 html 什么是Android属性动画 属性动画 Property Animation 系统是一个健壮的动画框架系统 它可以满足你大部分动画需求 不管动画对象是否
  • Spring Boot 使用及启动源码解析一

    前言 本篇文章会介绍Spring Boot 的基本原理 以及以及一些使用 常见的配置方式等 如何从单一架构延申到现在的前后端分离 垂直应用架构 的项目 从网站流量很小到现在的网站流量动则几百万上下的 发展 加速前端的架构 到后面 的分布式服
  • [QT编程系列-25]:多线程机制 - QThread和MoveToThread简介

    目录 第1章 简介 1 1 多线程的目的 1 2 QThread多线程使用方法 1 3 QT支持多线的步骤 第2章 QThread 2 1 概述 2 2 moveToThread 第1章 简介 1 1 多线程的目的 QThread类提供了一