公司网站后台怎么上传视频,wordpress 黑客主题,网站建设天津,网站建设 数据库连接目前(Qt5)常用的多线程的方式#xff1f; 1、派生于QThread然后重写run()函数 2、通过将派生QObject的类对象通过moveToThread()来移动到新的线程中 3、通过inherit QRunnable类然后重写run()方法、然后借助QThreadPool线程池来实现多线程 4、通过高级语法 QtConcurrent模块来…目前(Qt5)常用的多线程的方式 1、派生于QThread然后重写run()函数 2、通过将派生QObject的类对象通过moveToThread()来移动到新的线程中 3、通过inherit QRunnable类然后重写run()方法、然后借助QThreadPool线程池来实现多线程 4、通过高级语法 QtConcurrent模块来实现多线程 本文主要讲解不同多线程的使用方式并穿插不同之处和注意事项方便后来人的学习 在开始之前我们需要先明确几个概念对象和线程。对象指的是派生于QObject以及QThread类的实例化对象线程指的是多线程对象开辟出的新线程这个线程和主线程是两个并行存在。希望不要将对象和线程搞混了。 一、派生于QThread然后重写run()函数 这种方式是使用比较传统的方式直接上一个简单的demo:
#include QThreadclass Thread: public QThread
{
public:Thread(QObject* parentnullptr);signals:void signalNotify();public slots:void receiveMesg();protected:void run(){//do something}}/// widget.cpp ///
#include Thread.h
#include QApplication
Widget::Widget(QWidget* parent)
{Thread *t new Thread();t-start();
} 这是最基础的用法和结构派生于QThread、重写run()函数、创建线程对象以及开启线程。在这种方式下耗时操作都仍给了run()函数所以如果需求复杂一些就需要在run()中实现具体的业务。但是有几点我们需要注意 1不要在多线程中直接操作UI 2正确管理、使用定时器等资源 既然不能在run中直接操作UI那我们要是想把逻辑运算的结果通知到UI又该怎么操作呢通过信号槽的方式。这里要注意的是在主线程中创建线程对象后比如上面Thread实例化对象这个线程对象是属于主线程的所以在主线程中使用信号曹将线程对象和GUI对象连接起来后这并不是多线程通信真正子线程部分的是在run()中的逻辑 此外如果想在子线程中使用定时器一定要在run()中创建停止也要在子线程中操作切莫跨线程操作定时器。而且在run()中创建的资源都是属于子线程的对这些资源的操作一定要注意。在run()中连接的信号槽也是属于子线程的。 那想要在QThread中使用信号槽仅在run()中绑定信号槽就行了吗 不是必须在run()中通过调用exec()来开启事务循环。只有开启事务循环那么依赖于事务循环的种种特性定时器、信号槽、TCP通信、网络请求以及各种QEvent等才能使用明白了吧要是用不到上面那些特性只想执行一些耗时操作那么能不能不加exec()要是不加的话子线程在运行完耗时逻辑后就会结束线程。 在代码中看到在实例化Thread对象指针的时候没有指定parent那能指定parent吗不能为什么创建QThread派生类对象时候不能指定parent一方面源代码的实现中要求不能这么做如下
void QObject::moveToThread(QThread *targetThread)
{Q_D(QObject);if (d-threadData-thread.loadAcquire() targetThread) {// object is already in this threadreturn;}if (d-parent ! 0) {qWarning(QObject::moveToThread: Cannot move objects with a parent);return;}if (d-isWidget) {qWarning(QObject::moveToThread: Widgets cannot be moved to a new thread);return;}//do something else.....
} 还有就是存在潜在的风险如果指定了parent那么一旦parent生命周期结束了那势必要回收parent占用的资源这里面包括QThreadChild对象占用的资源。但是此时子线程很可能正在干活人家正在吃饭呢你把桌子掀了我想乌鸦也不会答应吧 既然没有指定parent就不能借助Qt的半自动内存回收机制那就需要人为的手动删除内存即通过QThread::finish信号来连接QThreadChild::deleteLater函数来实现对象资源的释放 二、通过将派生QObject的类对象通过moveToThread()来移动到新的线程中 这种方式适合于业务比较明确划分的情况通过将一类业务单独抽离成一个类然后将类的业务响应在多线程中执行。下面先上一个demo
#include QObjectclass Work: public QObject
{Q_OBJECTpublic:Work(QObject* parentnullptr);signals:void sigSendMsg(const QString);public slots:void receiveMsg(const QString msg);
}Widget::Widget(QWidget* parent)
{mWork new Work;connect(mWork, Work::sigSendMsg, this, Widget::slotFunc);workThread new QThread; connect(workThread, QThread::finish, mWork, Work::deleteLater);connect(workThread, QThread::finish, workThread, QThread::deleteLater);mWork-moveToThread(workThread)workThread-start();
}Widget::~Widget()
{workThread-quit();workThread-wait();
} 这种方式的核心就是moveToThread()moveToThread移动了什么是线程对象的归属权吗No是将线程对象中的槽函数放在了新线程中执行而线程对象依然属于创建它所在的线程中。切莫以为执行了moveToThread后线程对象所有的一切都打包给新线程了。在哪创建就属于哪在多线程中依然适用。使用moveToThread方式时、对象不能设置parent不然无法完成移动。 既然moveToThread也是借助于QThread那么如果此时有一个inherit QThread的子类ThreadA以及通过move方式到ThreadA中的线程B那这两个线程run()和线程B谁先执行呢通过测试发现run()先执行执行完run()后再执行move进来的槽函数。 三、通过inherit QRunnable类然后重写run()方法、然后借助QThreadPool线程池来实现多线程 直接上demo class HelloWorldTask : public QRunnable{void run() override{qDebug() Hello world from thread QThread::currentThread();}};HelloWorldTask *hello new HelloWorldTask();// QThreadPool takes ownership and deletes hello automaticallyQThreadPool::globalInstance()-start(hello); 这种方式的核心并不是如何使用而是了解线程池。线程池里有多少个正在干活的线程activeThreadCount这个池子又能放下多少个线程maxThreadCount要是现在没有多余的线程能够用、那么被丢进池子里的多线程任务又是如何处理的看QThreadPool了解。 开启多少个线程合适呢 线程的开辟和切换需要消耗CPU资源的尤其是涉及到CPU的上下文切换所以并不是线程开的越多越好那多少是理想值呢一般根据业务要求来有的是内核数量的4倍有的高达16倍。根据QThreadPool::maxThreadCount()来看这个于计算机的real and logic cores数量相关 四、通过高级语法 QtConcurrent模块来实现多线程 这种方式就很简单了适用于做一些纯属于简单的累活干完就拉到中间不需要交互过程一般都是配合lambda表达式使用用的时候别忘了在.pro中添加QT concurrent
QtConcurrent::run(); 拓展内容 关于currentThreadId()正确获取多线程id的方式
#include QCoreApplication
#ifdef Q_OS_LINUX#include pthread.h
#endif#ifdef Q_OS_WIN#include windows.h
#endifint main()
{#ifdef Q_OS_LINUXqDebug()pthread_self();#endif#ifdef Q_OS_WINqDebug()GetCurrentThreadId();#endif
} 多线程也是有优先级可以通过QThread::setPriority()来设置 此外还提供了QThread::isInterruptionRequested()来判断是否可以提前跳出线程循环
while(true)
{if(!isInterruptionRequested()){//耗时操作}
} 写在最后 上面介绍了常用的多线程方式那实际的工作中还有一个技巧就是不通过信号槽的方式在主线程中仍然能调用子线程函数的方式QMetaObject::invokeMethod参数可以指定是跨线程调用还是直接在同线程中调用。