Qt 异步事件序列算法

Qt 异步事件序列算法,qt,signals,Qt,Signals,我目前正在(重新)设计一个由几个部分组成的系统: 第一部分是一个实体,它(异步)从源获取帧,并在每次帧准备就绪时通过Qt信号触发事件 第二部分是GUI,实时显示帧,并通过插槽连接到上述信号 最后一部分是帧处理器,它的行为最好用顺序算法(while、for等)来描述,并且可以响应来自GUI的多种信号 主要问题是,是否有组织此类代码的最佳实践 我提出的解决方案是:运行4个线程 GUI线程,通过Qt信号与其他3个线程通信 帧源线程,它在每个帧可用性上发出一个信号 帧管理器线程,它根据信号存储帧,

我目前正在(重新)设计一个由几个部分组成的系统:

  • 第一部分是一个实体,它(异步)从源获取帧,并在每次帧准备就绪时通过Qt信号触发事件
  • 第二部分是GUI,实时显示帧,并通过插槽连接到上述信号
  • 最后一部分是帧处理器,它的行为最好用顺序算法(while、for等)来描述,并且可以响应来自GUI的多种信号
主要问题是,是否有组织此类代码的最佳实践

我提出的解决方案是:运行4个线程

  • GUI线程,通过Qt信号与其他3个线程通信
  • 帧源线程,它在每个帧可用性上发出一个信号
  • 帧管理器线程,它根据信号存储帧,并使用QWaitCondition通知帧处理器线程
  • 从帧管理器调用getNextFrame()的帧处理器线程

但我觉得这不是一个好的解决方案,可能是因为混合了不同的范例(等待条件和信号)。此外,等待条件可能会导致事件处理队列饥饿。

无需使用
QWaitCondition
。线程应该是裸线程(非派生的)
QThreads
。将所有代码放在移动到这些线程的
QObjects
中。帧管理器只向帧处理器发送信号。通过将事件发布到线程的事件循环,可以跨线程边界传递信号。这些事件循环在内部使用同步原语(互斥体)来实现访问的串行化,因此无需重新设计。QThread的
run()
的默认实现只是旋转一个事件循环,因此除了实例化线程并启动它之外,您不需要做任何事情

您的代码应该在GUI线程中实例化所有QObject的情况下运行,可能会降低性能。当基准测试显示哪些对象受CPU限制时,就可以将它们移动到单独的线程。这条规则有一个不幸但必要的例外:如果您的相机代码只能使用相机驱动程序提供的阻塞API(等待某个东西而不是异步报告它的API),那么您别无选择,只能为此牺牲一个线程。除了这种专用的阻塞解决方案线程之外,应用程序总共应该使用与可用内核数量相同的线程(
QThread::idealThreadCount()
)。您可以实例化多达该数量的线程,并在这些线程之间随机或以循环方式分发QObject。同样——只移动CPU绑定的QObject。使用非阻塞API(网络!)的IO绑定对象不需要这种处理。Sane设备驱动程序(如from)公开异步事件通知,这些通知可由转换为信号。它是Qt4.x中的一个私有API,但尽管如此,它仍然可以正常工作


很多常见的API都存在阻塞,这真是令人遗憾。数据库驱动程序就是一个典型的例子。例如,我移植了mysql客户端驱动程序以使用
qtcSocket
,这样数据库就不会阻塞使用它的线程。

我理解你的答案,它包含了很多对我有用的信息。然而,我的主要问题是如何将事件驱动范式(
frameReady()
events)转换为顺序处理(
while(getNextFrame())processFrame()
)。我遇到了启动本地事件循环(
QEventLoop
)并将所需信号(
frameReady()
)连接到其
quit()
插槽的解决方案。然而,在我看来,这看起来很难看,而且据我所知,它也有缺点——可能会干扰信号槽处理的正常过程。很抱歉,如果我在最初的公式中不够清楚的话。更具体地说,我希望能够表达一个算法:
frame=wait4goodFrame();/*在内部调用getNextFrame()*/doSmth(frame);doSmthElse(getNextFrame())而不是在插槽中处理它
void processFrame(frame){if(weAreWaiting4goodFrame&&isGoodFrame(frame))doSomth(frame);else if(WeareInstance2)doSmthElse(frame);else if…}
它的可读性较差,并且有大量的样板代码。将事件驱动转换为顺序处理正是您不应该做的。可读性/样板是最低限度的,除非您的代码微不足道。
if(weAreWaiting4goodFrame…
表示您需要一个状态机。最好直接在重新实现
void customEvent(QEvent*)中表达这些
。对于一个基本的FSM,只需分派到一个方法指针,每个状态有一个方法。这是正确设计它的方法。在顺序处理中弄乱状态机只在最初起作用。当您开始处理错误恢复等时,代码就会变成意大利面条。