Qt:线程与QObjects

1059 篇文章 288 订阅

QThread继承QObject。它发出信号来指示线程开始或者完成执行,并提供一些槽

另外,QObject可以在多个线程中使用,发出调用其他线程中的槽函数的信号,并将事件发送到“活动”在其他线程中的对象。这是可能的,因为每个线程都有自己的事件循环

QObject可重入

QObject是可重入的。它的大多数非gui子类,如QTimer、QTcpSocket、QUdpSocket和QProcess,也都是可重入的,因此可以在多个线程中同时使用这些类。注意,这些类被设计为在单个线程中创建和使用;在一个线程中创建一个对象,然后从另一个线程调用它的函数,这并不保证能够正常工作。有三个限制需要注意:

  • QObject的子对象必须始终在其父对象创建的线程中创建。这意味着,在其他事项中,您永远不应该将QThread对象(this)作为线程中创建的对象的父对象传递(因为QThread对象本身是在另一个线程中创建的)。
  • 事件驱动模块只能在单个线程中使用。具体来说,这适用于定时器机制网络模块。例如,您不能在非对象线程的线程中启动计时器或连接套接字。
  • 在删除QThread之前,必须确保删除线程中创建的所有对象。这可以通过在run()实现中创建堆栈上的对象轻松完成。

虽然QObject是可重入的,但是GUI类,特别是QWidget及其所有子类,是不可重入的. 他们只能用于主线程中,并且在主线程启动前必须调用QCoreApplication::exec()。

实际上,可以通过将耗时的操作放在单独的工作线程中,并在工作线程完成后在主线程的屏幕上显示结果,来轻松解决在主线程以外的其他线程中使用GUI类的可能性。这是用于实现Mandelbrot示例Blocking Fortune客户机示例的方法。

通常,在QApplication之前创建QObject是不被支持的,并且可能导致退出时奇怪的崩溃,这取决于平台。这意味着也不支持QObject的静态实例。一个结构合理的单线程或多线程应用程序应该使QApplication成为第一个创建、最后一个销毁的QObject。

线程事件循环

每个线程都可以有自己的事件循环。初始线程使用QCoreApplication::exec()或单个对话框的GUI应用程序启动事件循环,有时使用QDialog::exec()。其他线程可以使用QThread::exec()启动事件循环。与QCoreApplication类似,QThread提供了一个exit(int)函数和一个quit()槽。

线程中的事件循环使线程能够使用某些需要事件循环存在的非gui Qt类(如QTimer、QTcpSocket和QProcess)。还可以将来自任何线程的信号连接到特定线程的插槽。这在下面的“跨线程的信号和插槽”部分中进行了详细说明
在这里插入图片描述

  • QObject::thread: QObject存活于创建它的线程,事件传递到Object是由线程的事件循环分发的。可通过QObject::thread()查看QObject在那个线程。
  • QObject::moveToThread()函数更改对象及其子对象的线程相关性(如果对象有父对象,则不能移动该对象)。

从拥有该对象的线程以外的线程(或以其他方式访问该对象)在QObject上调用delete是不安全的,除非您保证该对象当时没有处理事件。使用QObject::deleteLater()来代替,然后将发布DeferredDelete事件,该事件最终会被对象线程的事件循环捡起。默认情况下,拥有QObject的线程是创建QObject的线程,但不是在调用QObject::moveToThread()之后。

如果没有运行事件循环,事件将不会被传递给对象。例如,如果在线程中创建了QTimer对象,但从未调用exec(),那么QTimer将永远不会发出超时()信号。调用deleteLater()也不起作用。(这些限制也适用于主线程。)

您可以随时使用线程安全函数QCoreApplication :: postEvent()将事件手动发布到任何线程中的任何对象。这些事件将由创建对象的线程的事件循环自动调度。

所有线程均支持事件过滤器,但有一个限制,即监视对象必须与被监视对象位于同一线程中。类似地,QCoreApplication :: sendEvent()(不同于postEvent())只能用于将事件调度到存在于从中调用该函数的线程中的对象。

从其他线程访问QObject子类

QObject及其所有子类都不是线程安全的。这包括整个事件交付系统。重要的是要记住,当您从另一个线程访问对象时,事件循环可能将事件交付给您的QObject子类。

如果你在一个QObject子类上调用一个不存在于当前线程中的函数,并且该对象可能接收到事件,你必须用互斥来保护所有对QObject子类内部数据的访问;否则,您可能会遇到崩溃或其他不希望发生的行为。

与其他对象一样,QThread对象生活在创建对象的线程中——而不是在调用QThread::run()时创建的线程中。在QThread子类中提供插槽通常是不安全的,除非使用互斥锁来保护成员变量。

另一方面,您可以安全地从QThread::run()实现发射信号,因为信号发射是线程安全的。

跨线程的信号和插槽

Qt支持以下信号插槽连接类型:

  • 自动连接Qt::AutoConnection(默认):
    • 如果接收器位于发出信号的线程中,则使用Qt :: DirectConnection。否则,将使用Qt :: QueuedConnection。发出信号时确定连接类型。
    • 也就是说:如果信号在接收对象具有亲和力的线程中发出,则其行为与直接连接相同。否则,行为与排队连接相同。”
  • 直接连接Qt::DirectConnection
    • 发出信号后立即调用插槽。该插槽在发射器的线程(信令线程)中执行,而不一定是接收器的线程。
  • 排队连接Qt::QueuedConnection
    • 当控制权返回到接收者线程的事件循环时,将调用该插槽。该插槽在接收者的线程中执行。
  • 阻塞队列连接Qt::BlockingQueuedConnection
    • 与Qt :: QueuedConnection相同,除了信令线程阻塞直到插槽返回为止。如果接收器位于信令线程中,则不得使用此连接,否则应用程序将死锁。
  • 唯一连接Qt::UniqueConnection
    • 这是一个标志,可以使用按位或将其与上述任何一种连接类型组合在一起。设置Qt :: UniqueConnection时,如果连接已经存在,即QObject :: connect()将返回false(即,如果同一对对象的相同信号已经连接到同一插槽)。这个标志是在Qt 4.6中引入的。

可以通过向connect()传递附加参数来指定连接类型。请注意,如果事件循环在接收方的线程中运行,则在发送方和接收方生活在不同线程中时使用直接连接是不安全的,这与在另一个线程中的对象上调用任何函数是不安全的原因相同。

QObject::connect()本身是线程安全的。

Mandelbrot示例使用队列连接在工作线程和主线程之间进行通信。为了避免冻结主线程的事件循环(以及应用程序的用户界面),所有的Mandelbrot分形计算都在一个单独的工作线程中完成。线程在完成分形渲染时发出信号。

类似地,Blocking Fortune客户端示例使用一个单独的线程与TCP服务器进行异步通信。

参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在Qt中,可以通过QObject::moveToThread()函数将对象及其子对象的线程相关性进行修改。这个函数可以将一个对象移动到指定的线程中。如果对象有父对象,那么就不能直接移动该对象。通过将对象移动到另一个线程中,可以在对象所在的线程中修改线程变量。举个例子,如果你想要修改一个线程中的变量,可以先将该对象移动到目标线程,然后在目标线程中修改变量的值。这样就可以在目标线程中修改线程变量了。另外,每个线程都可以有自己的事件循环,可以使用QThread::exec()启动事件循环。在事件循环中,可以处理线程中的事件,包括修改线程变量。123 #### 引用[.reference_title] - *1* *3* [Qt线程与QObjects](https://blog.csdn.net/zhizhengguan/article/details/115674671)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *2* [Qt线程同步:互斥锁QMutextLocker 、读锁 QReadWriteLock、信号量 QSemaphore、 条件变量...](https://blog.csdn.net/qq_16504163/article/details/130493328)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值