Qt源码阅读(二) moveToThread

打印 上一主题 下一主题

主题 852|帖子 852|积分 2556

Qt 源码分析之moveToThread

这一次,我们来看Qt中关于将一个QObject对象移动至一个线程的函数moveToThread

目录

Qt使用线程的基本方法

首先,我们简单的介绍一下在Qt中使用多线程的几种方法:

  • 重写QThread的run函数,将要在多线程执行的任务放到run函数里
  1. /*mythread.h*/
  2. #pragma once
  3. #include <QThread>
  4. class MyThread  : public QThread
  5. {
  6.     Q_OBJECT
  7. public:
  8.     explicit MyThread(QObject* parent = nullptr);
  9.     ~MyThread();
  10. protected:
  11.     void run() override;
  12. };
  13. /*mythread.cpp*/
  14. #include "mythread.h"
  15. #include <QDebug>
  16. MyThread::MyThread(QObject* parent)
  17.     : QThread(parent)
  18. {}
  19. MyThread::~MyThread()
  20. {}
  21. void MyThread::run()
  22. {
  23.     /*
  24.         在这个函数里执行耗时操作
  25.     */
  26.    for (auto a = 0; a < 10; a++) {
  27.        qDebug() << u8"线程";
  28.        QThread::sleep(1);
  29.    }
  30. }
  31. /*调用函数*/
  32. auto m_thread = new MyThread();
  33. // 调用start之后,就会去执行run里内容了
  34. m_thread->start();
复制代码
但是这种方法,不被Qt官方所推荐,Qt官方所推荐的是将对象移动至线程的方法moveToThread

  • 创建一个QThread对象,将对象移动至一个线程中,用信号槽的方式来触发该对象的槽函数,此时槽函数是在线程中执行的
  1. /*mytask.h*/
  2. #pragma once
  3. #include <QObject>
  4. class MyTask  : public QObject
  5. {
  6.     Q_OBJECT
  7. public:
  8.     MyTask(QObject *parent = nullptr);
  9.     ~MyTask();
  10. public slots:
  11.     void slotMyTask();
  12. };
  13. /*mytask.cpp*/
  14. #include "mytask.h"
  15. #include <QThread>
  16. #include <QDebug>
  17. MyTask::MyTask(QObject *parent)
  18.     : QObject(parent)
  19. {}
  20. MyTask::~MyTask()
  21. {}
  22. void MyTask::slotMyTask()
  23. {
  24.     /* 在这里执行耗时操作 */
  25.     for (auto a = 0; a < 10; a++) {
  26.         qDebug() << u8"当前线程: " << QThread::currentThread();
  27.         qDebug() << u8"线程";
  28.         QThread::sleep(1);
  29.     }
  30. }
  31. /*使用方法*/
  32. // 1. 创建任务对象以及线程对象
  33. auto m_task = new MyTask();
  34. auto* m_thread = new QThread();
  35. // 2. 将任务对象移动至线程
  36. m_task->moveToThread(m_thread);
  37. // 3. 将信号与任务类的槽连接起来
  38. connect(m_thread, &QThread::started, m_task, &MyTask::slotMyTask);
  39. //  4. 开启线程
  40. m_thread->start();
复制代码
此时,我们看到控制台会输出:
Cannot move objects with a parent (无法移动一个有父对象的object)

并且,我们能看到槽函数里打印的线程为主线程

  • 使用Qt的QtConcurrent,缺点之一是没有办法手动退出
  1. // 1. 创建一个有父对象的任务对象以及线程对象
  2. auto m_task = new MyTask(this);
  3. auto* m_thread = new QThread();
  4. // 2. 将任务对象移动至线程
  5. m_task->moveToThread(m_thread);
  6. // 3. 将信号与任务类的槽连接起来
  7. connect(m_thread, &QThread::started, m_task, &MyTask::slotMyTask);
  8. //  4. 开启线程
  9. m_thread->start();
复制代码

  • 对要移动的对象当前所属线程的一些判断:

    • 如果要移动的对象没有线程依附性,那么可以移动至目标线程
    • 如果移动操作所在线程与移动对象所在线程不一致,那么不允许去移动

  1. // 使用这个,需要在头文件里引入
  2. #include <QtConcurrent/QtConcurrent>
  3. // 定义一个任务函数
  4. int MainWindow::taskTest(int a)
  5. {
  6.     for (auto i = 1; i < 10; i++) {
  7.         qDebug() << "a: " << a;
  8.         QThread::sleep(1);
  9.     }
  10.     return 0;
  11. }
  12. /* 使用方法 */
  13. // 在函数后面跟上你要设置给函数的参数
  14. QtConcurrent::run(this, &MainWindow::taskTest, 10);
复制代码

  • 正式的移动操作
  1. // 当前对象已经在目标线程了
  2.     if (d->threadData.loadRelaxed()->thread.loadAcquire() == targetThread) {
  3.         // object is already in this thread
  4.         return;
  5.     }
  6.         // 不能移动一个有父对象的对象
  7.     if (d->parent != nullptr) {
  8.         qWarning("QObject::moveToThread: Cannot move objects with a parent");
  9.         return;
  10.     }
  11.         // 窗口部件不能移动到一个新的线程,在Qt里GUI操作只能在主线程
  12.     if (d->isWidget) {
  13.         qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");
  14.         return;
  15.     }
复制代码
一些线程和信号槽使用的心得

到了夹带私活时间,下面是一些多线程使用信号槽的一点小心得总结

  • 不能在子线程去更新UI界面,只能在主线程进行更新
  • 可以通过信号槽连接,在子线程通知主线程去更新UI
  • 跨线程使用信号槽,建议用QueuedConnection,因为这种连接方式,Qt会把信号丢到事件循环里去,这样槽函数会在接收者所在的线程执行。而DirectConnection这种连接方式,因为是直接回调槽函数,槽会在信号发出的线程进行调用。具体可看上篇关于信号与槽源码分析。
  • 但是使用QueuedConnection这种连接方式,信号的参数如果是自己定义的类型,一定要记得使用qRegisterMetaType来进行注册,或者使用Q_DECLARE_METATYPE来进行注册。否则,槽函数将不会触发。
  • BlockQueuedConnection这种方法慎用,因为如果信号发送者和接收者在同一个线程,将会导致死锁

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

渣渣兔

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表