单个线程中函数和槽之间的上下文切换?
Context switches between functions and slots in a single thread?
我正在实施一个小项目,我在其中广泛使用 QThreads 和 signals/slots(基本上是 Qt 5)。我可以说我很清楚 signals/slots 如何与 QThreads 一起工作。 (我已经在 StackExchange 上浏览了所有重要的 material 以及这些链接 "You are doing it wrong" 及其更新 You were not doing so wrong. I have also gone through Most correct way to use QThread)我确信我的设计我将不得不继承 QThread 但也添加插槽到子类(我知道它将 运行 在单独的线程中。)
我的调用线程(线程上的对象)托管至少三个不同 类 的此类插槽及其自己的实用程序函数。我的问题是:
线程关联的不同槽和函数之间的上下文切换是如何处理的?如果其中一个函数调用了 sleep 或 wait(应该是正确的?),是否会有上下文切换?其次,QThreads 有什么特定的行为吗?
如果 Qt 或 QThreads 没有什么特别之处,我仍然想了解这种行为的一般情况。
提前致谢。
我认为您对典型的事件驱动应用程序及其 运行-to-completion 事件处理程序所展示的协作式多任务处理感到困惑。这就是 WIN16、GEM、Atari TOS 和其他此类平台似乎在 没有 上下文切换的情况下进行多任务处理的方式。
我会在介绍这个故事的必要背景后尝试消除这种困惑。
上下文切换是一种在当前线程已用完其时间片或正在休眠时将可用核心重新用于 运行 另一个线程的方法。当您缺少内核时,这是一种优化。是的,它是您的代码 运行 所在平台的实现细节。它甚至不是 必要的:你不能仅仅通过观察它来判断你的平台上下文是否切换(真的,其他的边信道攻击)。
QThread
只是一个瘦线程控制器对象。受控线程是一个平台对象,Qt 不会改变它的行为。 QThread::run
的行为就像它在本机平台线程上一样,因为那是它 运行s.
QThread::run
方法仅旋转(exec()
用 Qt 的说法)一个事件循环(当然,如果您的重新实现不这样做,它就不会再这样做了)。
一个事件循环等待事件到达队列,然后通知目标 QObject
s 它们的接收。
跨线程(队列)信号槽连接是通过利用事件来实现的。发射时,信号复制其参数并将它们以 QMetaCallEvent
的形式发布到每个排队连接的接收器对象。由于这些对象位于另一个线程中,因此它们线程中的事件循环 运行 将被唤醒。然后它将拾取事件并由目标对象的 event
方法处理。具体来说,QObject::event
实现知道如何处理 QMetaCallEvent
:它将执行插槽调用。
因此,排队的插槽调用充当事件处理程序,因为它们是作为 QObject::event()
被事件循环调用的结果调用的。每当排队槽执行时,调用堆栈如下所示:
- 你的插槽方法,
QObject::event()
- ...
QEventLoop::exec()
QThread::run
- 特定于平台的线程函数。
那么,事件和事件处理程序如何给人多处理的印象?那是因为所有事件处理程序都是运行完成。他们从不休眠或阻塞,他们只是执行他们需要的短动作,并立即 return 进入事件循环。
一旦您的事件处理程序变成阻塞状态,此行为就会被破坏。由于通过排队连接(跨线程)调用的插槽实际上是 QMetaCallEvent
处理程序,如果您在其中 block/sleep/wait,您将休眠整个线程。假设您在插槽中调用 QThread::sleep
。调用堆栈是:
- 平台特定的睡眠实现,
QThread::sleep()
,
- 你的位置,
- ...
QThread::run()
.
此时,线程根本无法 运行启用。如果平台如此选择,另一个 线程可能 运行 在同一个核心上,以利用它,但这是平台的可选优化,您几乎无法控制。
线程关联的不同槽和函数之间的上下文切换是如何处理的?
有none个。或者,更具体地说,只要给定线程的事件队列中存储了 QMetaCallEvent
个事件,并且只要该线程是 运行 可用的,它就会继续执行排队的插槽调用。在一台空闲的多核机器上,您可以让 thead 执行排队的插槽调用,而无需额外的上下文切换。
如果您的槽休眠或等待,则整个线程休眠或等待。
如果其中一个函数调用了 sleep 或 wait,是否会有上下文切换?
不一定。您假设操作系统的可中断等待实现将抢占您的线程。可能是这样,也可能不是这样。当然,无论发生什么,您的线程此时都在休眠,并且在休眠期间显然不会发生任何其他事情。
我正在实施一个小项目,我在其中广泛使用 QThreads 和 signals/slots(基本上是 Qt 5)。我可以说我很清楚 signals/slots 如何与 QThreads 一起工作。 (我已经在 StackExchange 上浏览了所有重要的 material 以及这些链接 "You are doing it wrong" 及其更新 You were not doing so wrong. I have also gone through Most correct way to use QThread)我确信我的设计我将不得不继承 QThread 但也添加插槽到子类(我知道它将 运行 在单独的线程中。)
我的调用线程(线程上的对象)托管至少三个不同 类 的此类插槽及其自己的实用程序函数。我的问题是: 线程关联的不同槽和函数之间的上下文切换是如何处理的?如果其中一个函数调用了 sleep 或 wait(应该是正确的?),是否会有上下文切换?其次,QThreads 有什么特定的行为吗?
如果 Qt 或 QThreads 没有什么特别之处,我仍然想了解这种行为的一般情况。 提前致谢。
我认为您对典型的事件驱动应用程序及其 运行-to-completion 事件处理程序所展示的协作式多任务处理感到困惑。这就是 WIN16、GEM、Atari TOS 和其他此类平台似乎在 没有 上下文切换的情况下进行多任务处理的方式。
我会在介绍这个故事的必要背景后尝试消除这种困惑。
上下文切换是一种在当前线程已用完其时间片或正在休眠时将可用核心重新用于 运行 另一个线程的方法。当您缺少内核时,这是一种优化。是的,它是您的代码 运行 所在平台的实现细节。它甚至不是 必要的:你不能仅仅通过观察它来判断你的平台上下文是否切换(真的,其他的边信道攻击)。
QThread
只是一个瘦线程控制器对象。受控线程是一个平台对象,Qt 不会改变它的行为。 QThread::run
的行为就像它在本机平台线程上一样,因为那是它 运行s.
QThread::run
方法仅旋转(exec()
用 Qt 的说法)一个事件循环(当然,如果您的重新实现不这样做,它就不会再这样做了)。
一个事件循环等待事件到达队列,然后通知目标 QObject
s 它们的接收。
跨线程(队列)信号槽连接是通过利用事件来实现的。发射时,信号复制其参数并将它们以 QMetaCallEvent
的形式发布到每个排队连接的接收器对象。由于这些对象位于另一个线程中,因此它们线程中的事件循环 运行 将被唤醒。然后它将拾取事件并由目标对象的 event
方法处理。具体来说,QObject::event
实现知道如何处理 QMetaCallEvent
:它将执行插槽调用。
因此,排队的插槽调用充当事件处理程序,因为它们是作为 QObject::event()
被事件循环调用的结果调用的。每当排队槽执行时,调用堆栈如下所示:
- 你的插槽方法,
QObject::event()
- ...
QEventLoop::exec()
QThread::run
- 特定于平台的线程函数。
那么,事件和事件处理程序如何给人多处理的印象?那是因为所有事件处理程序都是运行完成。他们从不休眠或阻塞,他们只是执行他们需要的短动作,并立即 return 进入事件循环。
一旦您的事件处理程序变成阻塞状态,此行为就会被破坏。由于通过排队连接(跨线程)调用的插槽实际上是 QMetaCallEvent
处理程序,如果您在其中 block/sleep/wait,您将休眠整个线程。假设您在插槽中调用 QThread::sleep
。调用堆栈是:
- 平台特定的睡眠实现,
QThread::sleep()
,- 你的位置,
- ...
QThread::run()
.
此时,线程根本无法 运行启用。如果平台如此选择,另一个 线程可能 运行 在同一个核心上,以利用它,但这是平台的可选优化,您几乎无法控制。
线程关联的不同槽和函数之间的上下文切换是如何处理的?
有none个。或者,更具体地说,只要给定线程的事件队列中存储了
QMetaCallEvent
个事件,并且只要该线程是 运行 可用的,它就会继续执行排队的插槽调用。在一台空闲的多核机器上,您可以让 thead 执行排队的插槽调用,而无需额外的上下文切换。如果您的槽休眠或等待,则整个线程休眠或等待。
如果其中一个函数调用了 sleep 或 wait,是否会有上下文切换?
不一定。您假设操作系统的可中断等待实现将抢占您的线程。可能是这样,也可能不是这样。当然,无论发生什么,您的线程此时都在休眠,并且在休眠期间显然不会发生任何其他事情。