【问题标题】:Sequential algorithm with asynchronous events具有异步事件的顺序算法
【发布时间】:2012-07-01 11:00:25
【问题描述】:

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

  • 第一部分是一个实体,它(异步)从源获取帧并在每次帧准备就绪时触发一个事件 - 通过 Qt 信号。
  • 第二部分是实时显示帧的 GUI,并通过插槽连接到上述信号
  • 最后一部分是帧处理器,它的行为最好通过顺序算法(while、for 等)来描述,并有可能响应来自 GUI 的多种信号。

主要问题是是否有组织此类代码的最佳实践?

我想出但不太喜欢的解决方案是:运行 4 个线程。

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

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

【问题讨论】:

    标签: qt signals


    【解决方案1】:

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

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

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

    【讨论】:

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