qt帮助:

QThread 类提供了一种与平台无关的方法来管理线程。
        QThread 对象管理程序中的一个控制线程。 QThreads 在 run() 中开始执行。 默认情况下,run() 通过调用 exec() 启动事件循环,并在线程内运行 Qt 事件循环。
        您可以通过使用 QObject::moveToThread() 将工作对象移动到线程来使用它们。

实例1:

暂时不考虑多线程,先思考这样一个问题:想想我们平时会把耗时操作代码放在哪里?一个类中。那么有了多线程后,难道我们要把这段代码从类中剥离出来单独放到某个地方吗?显然这是很糟糕的做法。QObject 中的 moveToThread() 函数可以在不破坏类结构的前提下依然可以在新线程中运行。

 假设现在我们有个 QObject 的子类 Worker,这个类有个成员函数doWork(),该函数中运行的代码非常耗时。此时我要做的就是将这个类对象“移动”到新线程里,这样 Worker 的所有成员函数就可以在新线程中运行了。那么如何触发这些函数的运行呢?信号槽。在主线程里需要有个 signal 信号来关联并触发 Worker 的成员函数,与此同时 Worker 类中也应该有个 signal 信号用于向外界发送运行的结果。这样思路就清晰了,Worker 类需要有个槽函数用于执行外界的命令,还需要有个信号来向外界发送结果。如下列代码

class Worker : public QObject{Q_OBJECTpublic slots:void doWork(const QString &parameter) {QString result;/* ... here is the expensive or blocking operation ... */emit resultReady(result);}signals:void resultReady(const QString &result);};class Controller : public QObject{Q_OBJECTQThread 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 &);};

在作为“外界”的 Controller 类中,由于要发送命令与接收结果,因此同样是有两个成员:operate() 信号用于启动 Worker 类的耗时函数运行,handleResults() 槽函数用于接收新线程的运行结果。注意别和 Worker 类的两个成员搞混了,在本例中信号对应着槽,即“外界”的信号触发“新线程”的槽,“外界”的槽接收“新线程”的信号结果。

Worker slot内的代码将在单独的线程中执行。 但是,您可以自由地将 Worker 的slot连接到来自任何对象、任何线程中的任何信号。 由于一种称为queued connections的机制,跨不同线程连接信号和槽是安全的。

“移动到新线程”是一个很形象的描述,作为入门的认知是可以的,但是它的本质是改变线程亲和性(也叫关联性)。为什么要强调这一点?这是因为如果你天真的认为 Worker 类对象整体都移动到新线程中去了,那么你就会本能的认为 Worker 类对象的控制权是由新线程所属,然而事实并不是如此。「在哪创建就属于哪」这句话放在任何地方都是适用的。Worker 类对象是在 Controller 类中创建并初始化,因此该对象是属于主线程的。而 moveToThread() 函数的作用是将槽函数在指定的线程中被调用。当然,在新线程中调用函数的前提是该线程已经启动处于就绪状态,所以在上一节的 Controller 构造函数中,我们把各种信号槽连接起来后就可以启动新线程了。

使用 moveToThread() 有一些需要注意的地方,首先就是类对象不能有父对象,否则无法将该对象“移动”到新线程。如果类对象保存在栈上,自然销毁由操作系统自动完成;如果是保存在堆上,没有父对象的指针要想正常销毁,需要将线程的 finished() 信号关联到 QObject 的deleteLater() 让其在正确的时机被销毁。其次是该对象一旦“移动”到新线程,那么该对象中的计时器(如果有 QTimer 等计时器成员变量)将重新启动。不是所有的场景都会遇到这两种情况,但是记住这两个行为特征可以避免踩坑。

使代码在单独的线程中运行的另一种方法是继承 QThread 并重新实现 run()。 例如:

实例2:

class WorkerThread : public QThread{Q_OBJECTvoid run() override {QString result;/* ... here is the expensive or blocking operation ... */emit resultReady(result);}void do(){}//主线程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);connect(this, &MyObject::do, workerThread, &WorkerThread ::do);workerThread->start();}

为了验证只有run() 在新线程里运行,我们还写了一个do(),如果执行workerThread->do();则和run的线程id不同;do()在老的线程中执行;

        如果重写run()函数,则不会启动子线程的消息循环,需要自行执行exec();否则子线程收不到主线程发送的信号:如上面的

    connect(this, &MyObject::do, workerThread, &WorkerThread ::do);

则不会执行

源码:

class Q_CORE_EXPORT QThread : public QObject
{Q_OBJECT
public:static Qt::HANDLE currentThreadId() Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION;static QThread *currentThread();static int idealThreadCount() Q_DECL_NOTHROW;static void yieldCurrentThread();explicit QThread(QObject *parent = Q_NULLPTR);~QThread();//优先级enum Priority {IdlePriority,LowestPriority,LowPriority,NormalPriority,HighPriority,HighestPriority,TimeCriticalPriority,InheritPriority};void setPriority(Priority priority);Priority priority() const;bool isFinished() const;bool isRunning() const;void requestInterruption();bool isInterruptionRequested() const;void setStackSize(uint stackSize);uint stackSize() const;void exit(int retcode = 0);QAbstractEventDispatcher *eventDispatcher() const;void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);bool event(QEvent *event) Q_DECL_OVERRIDE;int loopLevel() const;public Q_SLOTS:void start(Priority = InheritPriority);//线程启动函数void terminate();void quit();//线程退出函数public:// default argument causes thread to block indefinetelybool wait(unsigned long time = ULONG_MAX);static void sleep(unsigned long);//静态函数  睡眠static void msleep(unsigned long);static void usleep(unsigned long);Q_SIGNALS:void started(QPrivateSignal);//信号void finished(QPrivateSignal);protected:virtual void run();int exec();static void setTerminationEnabled(bool enabled = true);protected:QThread(QThreadPrivate &dd, QObject *parent = Q_NULLPTR);private:Q_DECLARE_PRIVATE(QThread)friend class QCoreApplication;friend class QThreadData;
};

QThread::start

void QThread::start(Priority priority)
{Q_D(QThread);QMutexLocker locker(&d->mutex);if (d->isInFinish) {locker.unlock();wait();locker.relock();}if (d->running)return;d->running = true;d->finished = false;d->exited = false;d->returnCode = 0;d->interruptionRequested = false;//创建线程,线程函数是:QThreadPrivate::start/*NOTE: we create the thread in the suspended state, set thepriority and then resume the thread.since threads are created with normal priority by default, wecould get into a case where a thread (with priority less thanNormalPriority) tries to create a new thread (also with priorityless than NormalPriority), but the newly created thread preemptsits 'parent' and runs at normal priority.*/
#if defined(Q_CC_MSVC) && !defined(_DLL) // && !defined(Q_OS_WINRT)
#  ifdef Q_OS_WINRT// If you wish to accept the memory leaks, uncomment the part above.// See://  https://support.microsoft.com/en-us/kb/104641//  https://msdn.microsoft.com/en-us/library/kdzttdcb.aspx
#    error "Microsoft documentation says this combination leaks memory every time a thread is started. " \"Please change your build back to -MD/-MDd or, if you understand this issue and want to continue, " \"edit this source file."
#  endif// MSVC -MT or -MTd buildd->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,this, CREATE_SUSPENDED, &(d->id));
#else// MSVC -MD or -MDd or MinGW buildd->handle = (Qt::HANDLE) CreateThread(NULL, d->stackSize, (LPTHREAD_START_ROUTINE)QThreadPrivate::start,this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif // Q_OS_WINRTif (!d->handle) {qErrnoWarning(errno, "QThread::start: Failed to create thread");d->running = false;d->finished = true;return;}//设置优先级int prio;d->priority = priority;switch (d->priority) {case IdlePriority:prio = THREAD_PRIORITY_IDLE;break;case LowestPriority:prio = THREAD_PRIORITY_LOWEST;break;case LowPriority:prio = THREAD_PRIORITY_BELOW_NORMAL;break;case NormalPriority:prio = THREAD_PRIORITY_NORMAL;break;case HighPriority:prio = THREAD_PRIORITY_ABOVE_NORMAL;break;case HighestPriority:prio = THREAD_PRIORITY_HIGHEST;break;case TimeCriticalPriority:prio = THREAD_PRIORITY_TIME_CRITICAL;break;case InheritPriority:default:prio = GetThreadPriority(GetCurrentThread());break;}if (!SetThreadPriority(d->handle, prio)) {qErrnoWarning("QThread::start: Failed to set thread priority");}if (ResumeThread(d->handle) == (DWORD) -1) {qErrnoWarning("QThread::start: Failed to resume new thread");}
}

QThreadPrivate::start(void *arg)

nsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg)
{QThread *thr = reinterpret_cast<QThread *>(arg);QThreadData *data = QThreadData::get2(thr);qt_create_tls();TlsSetValue(qt_current_thread_data_tls_index, data);data->threadId.store(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));QThread::setTerminationEnabled(false);{QMutexLocker locker(&thr->d_func()->mutex);data->quitNow = thr->d_func()->exited;}if (data->eventDispatcher.load()) // custom event dispatcher set?data->eventDispatcher.load()->startingUp();elsecreateEventDispatcher(data);//创建 事件分发器#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINRT)// sets the name of the current thread.QByteArray objectName = thr->objectName().toLocal8Bit();qt_set_thread_name((HANDLE)-1,objectName.isEmpty() ?thr->metaObject()->className() : objectName.constData());
#endifemit thr->started(QThread::QPrivateSignal());//发射线程启动信号QThread::setTerminationEnabled(true);thr->run();//调用QThread::run()函数 -- 线程函数finish(arg);return 0;
}

实际上 run() 函数是在这里调用的,并且发出了 started() 启动信号,等到 run() 函数执行完毕,最后是调用了 QThreadPrivate::finish 函数结束线程,并且在finish内会发出 QThread::finished() 线程已结束的信号。

在start中创建了eventDispatcher用于消息循环

void QThreadPrivate::createEventDispatcher(QThreadData *data)
{
#ifndef Q_OS_WINRTQEventDispatcherWin32 *theEventDispatcher = new QEventDispatcherWin32;
#elseQEventDispatcherWinRT *theEventDispatcher = new QEventDispatcherWinRT;
#endifdata->eventDispatcher.storeRelease(theEventDispatcher);theEventDispatcher->startingUp();
}

QThread::run()

/*!The starting point for the thread. After calling start(), thenewly created thread calls this function. The defaultimplementation simply calls exec().You can reimplement this function to facilitate advanced threadmanagement. Returning from this method will end the execution ofthe thread.\sa start(), wait()
*/
void QThread::run()
{(void) exec();
}
/*!Enters the event loop and waits until exit() is called, returning the valuethat was passed to exit(). The value returned is 0 if exit() is called viaquit().This function is meant to be called from within run(). It is necessary tocall this function to start event handling.\sa quit(), exit()
*/
int QThread::exec()
{Q_D(QThread);QMutexLocker locker(&d->mutex);d->data->quitNow = false;if (d->exited) {d->exited = false;return d->returnCode;}locker.unlock();QEventLoop eventLoop;int returnCode = eventLoop.exec();locker.relock();d->exited = false;d->returnCode = -1;return returnCode;
}

在eventLoop.exec中进入消息循环:

int QEventLoop::exec(ProcessEventsFlags flags)
{
......while (!d->exit.loadAcquire())processEvents(flags | WaitForMoreEvents | EventLoopExec);ref.exceptionCaught = false;return d->returnCode.load();
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{Q_D(QEventLoop);if (!d->threadData->eventDispatcher.load())return false;return d->threadData->eventDispatcher.load()->processEvents(flags);
}

在这里便用到了之前创建的eventDispatcher;

每一个 Qt 应用程序至少有一个 事件循环 ,就是调用了 QCoreApplication::exec() 的那个事件循环。不过,QThread也可以开启事件循环。只不过这是一个受限于线程内部的事件循环。因此我们将处于调用main()函数的那个线程,并且由 QCoreApplication::exec() 创建开启的那个事件循环成为 主事件循环 ,或者直接叫 主循环 。注意,QCoreApplication::exec()只能在调用main()函数的线程调用。主循环所在的线程就是主线程,也被成为 GUI 线程,因为所有有关 GUI 的操作都必须在这个线程进行。QThread的局部事件循环则可以通过在 QThread::run() 中调用 QThread::exec() 开启。

我们通过以上源码可以看到,它的定义很简单,就是调用了一个函数:QThread::exec() 开启线程中的 事件循环 ,我们也可以通过继承QThread,重写run()函数的方式,让其实现相对复杂的逻辑代码。如果你的线程需要将某些槽函数在本线程完成的话,就必须开启事件循环,否则在线程内无法响应各种依赖事件循环的信号并作出相应的行为。

事件循环
若使用默认的 run() 方法或自行调用 exec() ,则QThread将开启事件循环。QThread 同样提供了 exit() 函数和 quit() 槽。这赋予了QThread使用需要事件循环的非GUI类的能力(QTimer、QTcpSocket 等)。也使得该线程可以关联任意一个线程的信号到指定线程的槽函数。如果一个线程没有开启事件循环,那么该线程中的 timeout() 将永远不会发射。

如果在一个线程中创建了OBject 对象,那么发往这个对象的事件将由该线程的事件循环进行分派。

传统图形界面应用程序都只有一个线程执行,并且一次执行一个操作。如果用户调用一个比较耗时的操作,就会冻结界面响应。一个解决方法是按照事件处理的思路:调用 Void QApplication::processEvents() 或 void QApplication::processEvents ( int maxtime ) 来强迫事件循环进行,但是这种做法是有潜在风险的。

按照QCoreApplication:processEvents()可能会引起递归,导致栈溢出崩溃的说法,当主线程在某个槽函数里正在执行processEvents时,刚好有一个能响应此槽函数的信号发送过(肯定是其他线程发的信号),这时就可能会发生可怕的递归,导致栈溢出崩溃。原因是processEvents在处理自己槽函数的事件时,又会调用到processEvents,进入到无尽的递归中。

另外一个解决方法是:采用多线程。

QT QThread多线程编程的方法一直有个争议,就是Bradley T. Hughes:You’re doing it wrong归纳为3中方法优劣问题:

方法1:
不使用事件循环。这是官方的 Manual 、example 以及相关书籍中都介绍的一种的方法。

子类化 QThread;
重载 run 函数,run函数内有一个 while 或 for 的死循环;
设置一个标记为来控制死循环的退出。
方法2:
这种方法也是Bradley T. Hughes极力批判的。

子类化 QThread;
重载 run 使其调用 QThread::exec();
并为该类定义信号和槽,这样一来,由于槽函数并不会在新开的 thread 运行,很多人为了解决这个问题在构造函数中调用 moveToThread(this);而争论和不解正是这样的一条语句造成的。
Bradley T. Hughes 给出说明是: QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。

方法3:
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。而从Qt4.4开始,qthreads-no-longer-abstract ,run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes推荐的方法。

类似于:

QThread thread;

Object obj;

Dummy dummy;

obj.moveToThread(&thread);

QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot()));

thread.start();

回到问题的本质,我们想要新开一个子线程来完成耗时的操作,有两个地方可以实现:

(1)子类化QThread的run()函数里,在run()函数里执行耗时的操作。因为run()函数是子线程的入口函数,一定不和主线程在同一个线程。就是方法1介绍的。如果在QThread子类里的slot里执行耗时操作,因为slot是在主线程中执行的,所以行不通。

(2)利用方法(3),子类化QObject ,如object。在object的slot里执行耗时操作。但是slot仍在主线程里执行,怎么办?QThread *thread;object.moveToThread(thread); 这样,这个QObject的slot都会在子线程里执行,达到了和主线程区分开的目的啦。

Qt-QThread相关推荐

  1. Qt QThread

    1)QThread 的l两种使用方法: 方式一: a. 子类化 QThread(不使用事件循环),创建对象,并调用start()函数. b. 重载 run 函数,run函数内有一个while或for的 ...

  2. Qt QThread安全退出

    文章目录 QThread的使用方式 QThread的退出方式 QThread安全退出 安全的退出方式 线程创建以及退出示例 QThread的使用方式 在Qt中,使用QThread实现子线程的方式有两种 ...

  3. QT Qthread中止回收

    在QT开辟一个QThrea线程时,想要中止并且回收资源,一般这样写: //退出线程 recThread->quit(); //回收资源 recThread->wait(); delete ...

  4. Qt oracle消息队列,QT QThread 线程间消息队列

    QT 提供了良好的开发包:线程间也可以实现消息队列的方式 类似 windows PostThreadMessage的工作方式.同时比普通队列简单的很多. 但是需要稍微做特殊处理: 1. CMyThre ...

  5. 使用Qt的多线程编程

    Qt的多线程编程 项目简介 项目技术 项目展示 主要源码片段解析 获取完整项目源码传送门 项目简介 我们展示了如何将自定义类型与元对象系统集成在一起,使它们能够存储在QVariant对象中,在调试信息 ...

  6. QThread多线程编程分析

    QThread多线程编程分析 传统图形界面应用程序都只有一个线程执行,并且一次执行一个操作.如果用户调用一个比较耗时的操作,就会冻结界面响应.一个解决方法是按照事件处理的思路:调用 Void QApp ...

  7. QThread :Destroyed while thread is still running

    程序运行报错:QThread :Destroyed while thread is still running pyqt在ui界面中,查db的大量数据会卡顿 用,必须用QThread解决 今天运行界面 ...

  8. 服务器可以pyqt显示吗,用pyqt+socket实现远程操作服务器的一个例子,PyQtsocket,方法,示例...

    @本文来源于公众号:csdn2299,喜欢可以关注公众号 程序员学府 这篇文章主要介绍了PyQt+socket实现远程操作服务器的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定 ...

  9. qtextedit 默认文案_QT中常用的控件说明.md

    2主窗口类型 有三种窗口 QMainWindow QWiget QDialog QMainWindow:可以包含菜单栏,工具栏和标题栏,是 最常见的窗口显示形式. QDialog:是对话窗口的基类.没 ...

  10. python:写个简陋的按键精灵

    1.介绍说明 本程序类似于按键精灵,代替用户执行重复性的的按键.输入文本.鼠标点击功能,可设置时间延迟. 示例如图: 使用流程: 1.共有ABCDEF六套方案,用户编辑任意方案 2.点击打开,可打开之 ...

最新文章

  1. 【Python3_进阶系列_006】Python3-单例模式
  2. 在Mac OS X中配置Apache + PHP + MySQL
  3. Linux环境下Redis集群实践
  4. 查询数据库中的表结构信息
  5. ServiceStack.Redis——Redis于.net向上client解
  6. (网址收藏)Golang模块之HTTP
  7. 【Python爬虫】写个爬虫爬取自己的博客,可以刷访问量
  8. koa2 mysql 中间件_Koa2 和 Express 中间件对比
  9. Linux文档阅读笔记-cut与sort的基本用法
  10. [EMU8086]基于8086的汇编语言学习
  11. react-native 路由 react-native-router-flux
  12. C#LeetCode刷题-剑指Offer
  13. VS2010编译驱动程序
  14. Linux命令:more
  15. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-2.微信扫一扫功能开发前期准备...
  16. 20155201 2016-2017-2 《Java程序设计》第五周学习总结
  17. [软件推荐]一款win7 x86 x64都能用的免费内存虚拟盘的小工具——ImDisk
  18. 在线抽奖、限时秒杀、拼团抢购,别的商城有的功能你也可以轻松拥有!
  19. Java之XML转Json
  20. 2021阿里云ECS镜像导入本地VMware虚拟机

热门文章

  1. Java MD5加密工具类
  2. Java VM: Java HotSpot(TM) 64-Bit Server VM (25.261-b12 mixed mode windows-amd64 compressed oops)
  3. Java项目经验相关常见面试题
  4. 谈谈优化JDBC数据库编程(转)
  5. 【屏幕PrtSc键的使用,截取对话框和全屏截取】
  6. 【开源】3串锂电池充放电保护板设计参考
  7. rainmeter雨滴皮肤——万花筒
  8. 期货反向跟单这个模式、大家目前都耳熟能详,操作原理也算是人尽皆知了!
  9. 做数据迁移差点累死的程序员有话要说----数据迁移经验分享
  10. 产品出口到沙特要做SGS或BV的清关SASO证书,请问哪里办理最迅速?