• 基于Qt 5.14.2的QThread多线程用法及实例解析


    在 qt 中使用了多线程,有些事项是需要额外注意的:

    1.默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新;
    2.子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理;
    3.主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制。

    1. 线程类 QThread

    Qt 中提供了一个线程类,通过这个类就可以创建子线程了,Qt 中一共提供了两种创建子线程的方式,后边会依次介绍其使用方式。先来看一下这个类中提供的一些常用 API 函数:

    1.1 常用共用成员函数

    1. // QThread 类常用 API
    2. // 构造函数
    3. QThread::QThread(QObject *parent = Q_NULLPTR);
    4. // 判断线程中的任务是不是处理完毕了
    5. bool QThread::isFinished() const;
    6. // 判断子线程是不是在执行任务
    7. bool QThread::isRunning() const;
    8. // 退出线程的工作函数
    9. void QThread::exit(int returnCode = 0);
    10. // 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
    11. // 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
    12. bool QThread::wait(unsigned long time = ULONG_MAX);

    1.2 信号槽

    1. // 和调用 exit() 效果是一样的
    2. // 代用这个函数之后, 再调用 wait() 函数
    3. [slot] void QThread::quit();
    4. // 启动子线程
    5. [slot] void QThread::start(Priority priority = InheritPriority);
    6. // 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
    7. [slot] void QThread::terminate();
    8. // 线程中执行的任务完成了, 发出该信号
    9. // 任务函数中的处理逻辑执行完毕了
    10. [signal] void QThread::finished();
    11. // 开始工作之前发出这个信号, 一般不使用
    12. [signal] void QThread::started();

    1.3 静态函数

    1. // 返回一个指向管理当前执行线程的QThread的指针
    2. [static] QThread *QThread::currentThread();
    3. // 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同
    4. [static] int QThread::idealThreadCount();
    5. // 线程休眠函数
    6. [static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒
    7. [static] void QThread::sleep(unsigned long secs); // 单位: 秒
    8. [static] void QThread::usleep(unsigned long usecs); // 单位: 微秒

    1.4 任务处理函数

    这个 run() 是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承 QThread,并且在子类中重写父类的 run() 方法,函数体就是对应的任务处理流程。另外,这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数 start() 启动子线程,当子线程被启动,这个 run() 函数也就在线程内部被调用了。

    1. // 子线程要处理什么任务, 需要写到 run() 中
    2. [virtual protected] void QThread::run();

    实际使用方法1:

    mythread.h:

    1. #ifndef MYTHREAD_H
    2. #define MYTHREAD_H
    3. #include
    4. class MyThread : public QThread
    5. {
    6. Q_OBJECT
    7. public:
    8. explicit MyThread(QObject *parent = nullptr);
    9. protected:
    10. void run();
    11. signals:
    12. // 自定义信号, 传递数据
    13. void curNumber(int num);
    14. public slots:
    15. };
    16. #endif // MYTHREAD_H

    mmythread.cpp

    1. #include "mythread.h"
    2. #include
    3. MyThread::MyThread(QObject *parent) : QThread(parent)
    4. {
    5. }
    6. void MyThread::run()
    7. {
    8. qDebug() << "当前线程对象的地址: " << QThread::currentThread();
    9. int num = 0;
    10. while(1)
    11. {
    12. emit curNumber(num++);
    13. if(num == 10000000)
    14. {
    15. break;
    16. }
    17. QThread::usleep(1);
    18. }
    19. qDebug() << "run() 执行完毕, 子线程退出...";
    20. }

    mainwindow.cpp

    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3. #include "mythread.h"
    4. #include
    5. MainWindow::MainWindow(QWidget *parent) :
    6. QMainWindow(parent),
    7. ui(new Ui::MainWindow)
    8. {
    9. ui->setupUi(this);
    10. qDebug() << "主线程对象地址: " << QThread::currentThread();
    11. // 创建子线程
    12. MyThread* t1= new MyThread;
    13. connect(t1, &MyThread::curNumber, this, [=](int num)
    14. {
    15. ui->label->setNum(num);
    16. });
    17. connect(ui->startBtn, &QPushButton::clicked, this, [=]()
    18. {
    19. // 启动子线程
    20. t1->start();
    21. });
    22. }
    23. MainWindow::~MainWindow()
    24. {
    25. delete ui;
    26. }

     


    实际使用方法2:

    mythread.h:

    1. #ifndef MYTHREAD_H
    2. #define MYTHREAD_H
    3. #include
    4. #include
    5. // 生成随机数
    6. class Generate : public QObject
    7. {
    8. Q_OBJECT
    9. public:
    10. explicit Generate(QObject *parent = nullptr);
    11. void working(int num);
    12. signals:
    13. void sendArray(QVector<int> num);
    14. };
    15. class BubbleSort : public QObject
    16. {
    17. Q_OBJECT
    18. public:
    19. explicit BubbleSort(QObject *parent = nullptr);
    20. void working(QVector<int> list);
    21. signals:
    22. void finish(QVector<int> num);
    23. };
    24. class QuickSort : public QObject
    25. {
    26. Q_OBJECT
    27. public:
    28. explicit QuickSort(QObject *parent = nullptr);
    29. void working(QVector<int> list);
    30. private:
    31. void quickSort(QVector<int> &list, int l, int r);
    32. signals:
    33. void finish(QVector<int> num);
    34. };
    35. #endif // MYTHREAD_H

    mythread.cpp:

    1. #include "mythread.h"
    2. #include
    3. #include
    4. #include
    5. Generate::Generate(QObject *parent) : QObject(parent)
    6. {
    7. }
    8. void Generate::working(int num)
    9. {
    10. qDebug() << "生成随机数的线程的线程地址: " << QThread::currentThread();
    11. QVector<int> list;
    12. QElapsedTimer time;
    13. time.start();
    14. for(int i=0; i
    15. {
    16. list.push_back(qrand() % 100000);
    17. }
    18. int milsec = time.elapsed();
    19. qDebug() << "生成" << num << "个随机数总共用时:" << milsec << "毫秒";
    20. emit sendArray(list);
    21. }
    22. BubbleSort::BubbleSort(QObject *parent) : QObject(parent)
    23. {
    24. }
    25. void BubbleSort::working(QVector<int> list)
    26. {
    27. qDebug() << "冒泡排序的线程的线程地址: " << QThread::currentThread();
    28. QElapsedTimer time;
    29. time.start();
    30. int temp;
    31. for(int i=0; isize(); ++i)
    32. {
    33. for(int j=0; jsize()-i-1; ++j)
    34. {
    35. if(list[j] > list[j+1])
    36. {
    37. temp = list[j];
    38. list[j] = list[j+1];
    39. list[j+1] = temp;
    40. }
    41. }
    42. }
    43. int milsec = time.elapsed();
    44. qDebug() << "冒泡排序用时" << milsec << "毫秒";
    45. emit finish(list);
    46. }
    47. QuickSort::QuickSort(QObject *parent) : QObject(parent)
    48. {
    49. }
    50. void QuickSort::working(QVector<int> list)
    51. {
    52. qDebug() << "快速排序的线程的线程地址: " << QThread::currentThread();
    53. QElapsedTimer time;
    54. time.start();
    55. quickSort(list, 0, list.size()-1);
    56. int milsec = time.elapsed();
    57. qDebug() << "排序用时" << milsec << "毫秒";
    58. emit finish(list);
    59. }
    60. void QuickSort::quickSort(QVector<int> &s, int l, int r)
    61. {
    62. if (l < r)
    63. {
    64. int i = l, j = r;
    65. // 拿出第一个元素, 保存到x中,第一个位置成为一个坑
    66. int x = s[l];
    67. while (i < j)
    68. {
    69. // 从右向左找小于x的数
    70. while (i < j && s[j] >= x)
    71. {
    72. //左移, 直到遇到小于等于x的数
    73. j--;
    74. }
    75. if (i < j)
    76. {
    77. //将右侧找到的小于x的元素放入左侧坑中, 右侧出现一个坑
    78. //左侧元素索引后移
    79. s[i++] = s[j];
    80. }
    81. // 从左向右找大于等于x的数
    82. while (i < j && s[i] < x)
    83. {
    84. //右移, 直到遇到大于x的数
    85. i++;
    86. }
    87. if (i < j)
    88. {
    89. //将左侧找到的元素放入右侧坑中, 左侧出现一个坑
    90. //右侧元素索引向前移动
    91. s[j--] = s[i];
    92. }
    93. }
    94. //此时 i=j,将保存在x中的数填入坑中
    95. s[i] = x;
    96. quickSort(s, l, i - 1); // 递归调用
    97. quickSort(s, i + 1, r);
    98. }
    99. }

    mainwindow.h:

    1. #ifndef MAINWINDOW_H
    2. #define MAINWINDOW_H
    3. #include
    4. QT_BEGIN_NAMESPACE
    5. namespace Ui { class MainWindow; }
    6. QT_END_NAMESPACE
    7. class MainWindow : public QMainWindow
    8. {
    9. Q_OBJECT
    10. public:
    11. MainWindow(QWidget *parent = nullptr);
    12. ~MainWindow();
    13. signals:
    14. void starting(int num);
    15. private:
    16. Ui::MainWindow *ui;
    17. };
    18. #endif // MAINWINDOW_H

    mainwindow.cpp:

    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3. #include "mythread.h"
    4. #include
    5. MainWindow::MainWindow(QWidget *parent)
    6. : QMainWindow(parent)
    7. , ui(new Ui::MainWindow)
    8. {
    9. ui->setupUi(this);
    10. // 1. 创建子线程对象
    11. QThread* t1 = new QThread;
    12. QThread* t2 = new QThread;
    13. QThread* t3 = new QThread;
    14. // 2. 创建任务类的对象
    15. Generate* gen = new Generate;
    16. BubbleSort* bubble = new BubbleSort;
    17. QuickSort* quick = new QuickSort;
    18. // 3. 将任务对象移动到某个子线程中
    19. gen->moveToThread(t1);
    20. bubble->moveToThread(t2);
    21. quick->moveToThread(t3);
    22. connect(this, &MainWindow::starting, gen, &Generate::working);
    23. // 2. 启动子线程
    24. connect(ui->start, &QPushButton::clicked, this, [=]()
    25. {
    26. emit starting(10000);
    27. t1->start();
    28. });
    29. connect(gen, &Generate::sendArray, bubble, &BubbleSort::working);
    30. connect(gen, &Generate::sendArray, quick, &QuickSort::working);
    31. // 接收子线程发送的数据
    32. connect(gen, &Generate::sendArray, this, [=](QVector<int> list){
    33. t2->start();
    34. t3->start();
    35. for(int i=0; isize(); ++i)
    36. {
    37. ui->randList->addItem(QString::number(list.at(i)));
    38. }
    39. });
    40. connect(bubble, &BubbleSort::finish, this, [=](QVector<int> list){
    41. for(int i=0; isize(); ++i)
    42. {
    43. ui->bubbleList->addItem(QString::number(list.at(i)));
    44. }
    45. });
    46. connect(quick, &QuickSort::finish, this, [=](QVector<int> list){
    47. for(int i=0; isize(); ++i)
    48. {
    49. ui->quickList->addItem(QString::number(list.at(i)));
    50. }
    51. });
    52. connect(this, &MainWindow::destroy, this, [=]()
    53. {
    54. t1->quit();
    55. t1->wait();
    56. t1->deleteLater(); // delete t1;
    57. t2->quit();
    58. t2->wait();
    59. t2->deleteLater();
    60. t3->quit();
    61. t3->wait();
    62. t3->deleteLater();
    63. gen->deleteLater();
    64. bubble->deleteLater();
    65. quick->deleteLater();
    66. });
    67. }
    68. MainWindow::~MainWindow()
    69. {
    70. delete ui;
    71. }

    main.cpp:

    1. #include "mainwindow.h"
    2. #include
    3. int main(int argc, char *argv[])
    4. {
    5. QApplication a(argc, argv);
    6. qRegisterMetaTypeint>>("QVector");
    7. MainWindow w;
    8. w.show();
    9. return a.exec();
    10. }

    详情代码请见:

    https://download.csdn.net/download/weixin_50016546/86931193

  • 相关阅读:
    访问一次网站的全过程
    全面解析HTTPS协议
    9月19日作业
    GBase8s分片表操作实践
    R语言拟合ARIMA模型并使用拟合模型进行预测推理、使用autoplot函数可视化ARIMA模型预测结果(返回的对象为ggplot2对象)
    python调用海康工业相机实现拍一张图片,支持调整曝光、增益、帧率
    天啦,从Mongo到ClickHouse我到底经历了什么?
    个人信息保护专业人员认证(CCRC-PIPP)
    Reactjs数据篇
    【常用代码15】文字单词超出强制分割换行,word-break: break-all;和word-wrap: break-word;的区别
  • 原文地址:https://blog.csdn.net/weixin_50016546/article/details/127749541