我正在实施一个小项目,我正在广泛使用QThreads和信号/插槽(基本上是Qt 5)。我可以说我对信号/插槽如何与QThreads一起工作有一个很好的想法。 (我已经浏览了StackExchange上的所有重要资料以及这些链接“你做错了”及其更新You were not doing so wrong。我也经历了Most correct way to use QThread)我确信我的设计是我的将必须子类化QThread,但也向子类添加插槽(我知道它将在不同的线程中运行。)
我的调用线程(线程上的对象)承载至少三个不同类的插槽以及它自己的实用程序函数。我的问题是: 如何处理上下文在不同插槽之间切换以及与线程相关的函数?如果其中一个函数有睡眠或等待调用(应该是对的?),是否会有一个上下文切换?其次QThreads有什么特定的行为吗?
如果Qt或QThreads没有任何特定内容,我仍然想知道这种行为。 提前谢谢。
答案 0 :(得分:1)
我认为您对典型的事件驱动应用程序展示的协作式多任务处理感到困惑,因为它们具有运行完成事件处理程序。这就是WIN16,GEM,Atari TOS和其他此类平台如何与 no 上下文切换进行多任务处理。
在揭露这个故事的必要背景之后,我会尝试清除这种混乱。
上下文切换是一种在当前线程用完其时间片或正在休眠时重用可用内核来运行另一个线程的方法。当你缺少核心时,它是排序的优化。是的,它是运行代码的平台的实现细节。它甚至不是必要的:你无法通过查看它来判断你的平台上下文是否切换(实际上,除了侧通道攻击之外)。
QThread
只是一个瘦线程控制器对象。受控线程是一个平台对象,Qt不会改变它的行为。 QThread::run
的行为就像在本机平台线程上一样,因为它就是它运行的地方。
QThread::run
方法仅在Qt用语中旋转(exec()
)一个事件循环(当然,如果你的重新实现没有,它将不会再这样做了。)
事件循环等待事件到达队列,然后通知目标QObject
他们的接收。
跨线程(排队)信号槽连接是通过利用事件来实现的。在发射时,信号复制其参数并将它们以QMetaCallEvent
发布到每个排队连接的接收器对象。由于这些对象存在于另一个线程中,因此在其线程中运行的事件循环将被唤醒。然后它将拾取事件并由目标对象的event
方法处理。具体来说,QObject::event
实现知道如何处理QMetaCallEvent
:它将执行插槽调用。
因此,排队的插槽调用充当事件处理程序,因为它们被调用为事件循环调用QObject::event()
的效果。每当排队的插槽执行时,调用堆栈如下所示:
QObject::event()
QEventLoop::exec()
QThread::run
那么,事件和事件处理程序如何提供多处理的印象?这是因为所有事件处理程序都正在运行完成。他们从不睡觉或阻止,只是执行他们需要的短动作,并立即返回事件循环。
一旦事件处理程序阻塞,此行为就会被破坏。由于通过排队连接调用的插槽(跨线程)是有效的QMetaCallEvent
处理程序,如果你阻塞/休眠/等待它们,你就会睡眠整个线程。假设您在插槽中拨打QThread::sleep
。然后调用堆栈是:
QThread::sleep()
,QThread::run()
。此时,线程根本不可运行。如果平台如此选择,另一个线程可能在同一个核心上运行,以便使用它,但这是您无法控制的平台可选优化。
如何处理上下文在不同的插槽之间切换以及与线程相关的函数?
没有。或者,更具体地说,只要在给定线程的事件队列中存储QMetaCallEvent
个事件,并且只要该线程可运行,它就会继续执行排队的槽调用。在其他空闲的多核计算机上,您可以保持aad执行排队的时隙调用而不需要额外的上下文切换。
如果你的插槽休眠或等待,整个线程都会休眠或等待。
如果其中一个函数有睡眠或等待调用,是否会有上下文切换?
不一定如此。您假设操作系统的可中断等待的实现将抢占您的线程。情况可能如此,或者情况可能并非如此。无论发生什么,当然,你的线程在那个时候正在睡觉,显然在它睡觉时没有其他任何事情发生。