C++ 当接收器忙时,Qt信号会发生什么情况?
在我的应用程序中,我有一个C++ 当接收器忙时,Qt信号会发生什么情况?,c++,multithreading,qt,qt-signals,qtcore,C++,Multithreading,Qt,Qt Signals,Qtcore,在我的应用程序中,我有一个QTimer实例,它的timeout()信号连接到主窗口对象中的一个插槽,导致它定期被调用。插槽使用相机拍摄照片并将其保存到磁盘 我想知道当接收器(主线程上的窗口对象)当前正忙(如拍摄和保存前一张照片)时,如果信号发出(我猜想是从执行QTimer的单独线程发出),会发生什么情况。在前一个调用终止后,调用是否排队并执行?整个想法是让它定期运行,但是当控件返回到事件循环时,这些调用是否会排队,然后被随机调用,从而造成混乱?我怎样才能避免呢?从理论上讲,插槽应该执行得很快,但
QTimer
实例,它的timeout()
信号连接到主窗口对象中的一个插槽,导致它定期被调用。插槽使用相机拍摄照片并将其保存到磁盘
我想知道当接收器(主线程上的窗口对象)当前正忙(如拍摄和保存前一张照片)时,如果信号发出(我猜想是从执行QTimer
的单独线程发出),会发生什么情况。在前一个调用终止后,调用是否排队并执行?整个想法是让它定期运行,但是当控件返回到事件循环时,这些调用是否会排队,然后被随机调用,从而造成混乱?我怎样才能避免呢?从理论上讲,插槽应该执行得很快,但假设硬件出现了一些问题,出现了一个暂停
在这种情况下,我希望调用被丢弃而不是排队,更有用的是在调用发生时做出反应的能力(警告用户,终止执行)。您可以使用connect方法的Qt:(Blocking)QueuedConnection
连接类型来避免立即触发的直接连接
因为您有单独的线程,所以应该使用阻塞版本。但是,当您希望避免直接调用而不需要单独的线程时,应该考虑非阻塞变体。
详情请参阅
为方便起见,请参阅文档:
Qt::QueuedConnection
当控件返回到接收方线程的事件循环时,将调用该插槽。插槽在接收器的线程中执行
Qt::BlockingQueuedConnection
与QueuedConnection相同,除了当前线程阻塞,直到插槽返回。此连接类型仅适用于发射器和接收器位于不同线程中的情况
您可能想写的是,您不希望直接连接而不是排队
QCoreApplication::removePostedEvents(QObject*receiver,int eventType)
可以使用事件类型为MetaCall
的事件,或者在队列被那些繁重的任务饱和时清理队列。此外,如果设置了退出插槽,则始终可以使用标志与插槽进行通信
有关详细信息,请参见以下论坛讨论:答案是肯定的。当QTimer和接收方处于不同的线程中时,调用被放入接收方事件队列中。如果您的拍照或保存例程占用了执行时间,那么您的事件可能会被严重延迟。但这对所有事件都是一样的。如果例程没有将控制权交还给事件循环,gui将挂起。您可以使用: Qt::BlockingQueuedConnection与QueuedConnection相同,除了 当前线程阻塞,直到插槽返回。此连接类型 只能在发射器和接收器处于不同位置时使用 线程
但像这样的情况很可能是暗示你的逻辑有问题。此时的其他答案都有相关的背景。但需要知道的关键是,如果计时器回调正在向不同线程中的插槽发送信号,那么该连接要么是QueuedConnection,要么是BlockingQueuedConnection 因此,如果您使用计时器尝试完成某种常规处理,那么在计时器启动和插槽实际执行之间,这会给您带来一些额外的时间抖动,因为接收对象在它自己的线程中运行一个独立的事件循环。这意味着当事件被放入队列时,它可以执行任意数量的其他任务,直到它完成处理这些事件,图片线程才会执行计时器事件 计时器应与照片逻辑位于同一线程中。将计时器与摄影机快照放在同一线程中,可以直接连接,并在计时间隔上提供更好的稳定性。特别是如果照片捕获和保存偶尔有特殊的持续时间 假设间隔为10秒,它是这样的:
- 将计时器设置为10秒
- 定时器点火
- 节省开始时间
- 拍照
- 将照片保存到磁盘(假设由于某些奇怪的原因需要3秒钟)
- 计算10-(当前时间-开始时间)=7秒
- 设定7秒的时间
您还可以在此处设置一些逻辑来检测跳过的间隔(例如,其中一个操作需要11秒才能完成…在进行了一些实验后,我在此处详细介绍了当接收器忙时,
QTimer
的行为
以下是实验源代码:(将QT+=testlib
添加到项目文件)
由于接收器忙,仅错过一次单击时
n[2] = 1500; // small stall (longer than 1sec, but less than 2sec)
n[2] = 2500; // big stall (more than 2sec)
然后,在暂停结束后立即调用下一个插槽,但后续调用仍然是1000毫秒的倍数:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 900
leaving: 2901
entering: 3000
sleeping: 200
leaving: 3201
entering: 4000
sleeping: 200
leaving: 4201
输出:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 1450
leaving: 3451 // one timer click is missed (3451 > 3000)
entering: 3451 // hence, the following execution happens right away
sleeping: 1450
leaving: 4901 // one timer click is missed (4901 > 4000)
entering: 4902 // hence, the following execution happens right away
sleeping: 200
leaving: 5101 // one timer click is missed (5101 > 5000)
entering: 5101 // hence, the following execution happens right away
sleeping: 200
leaving: 5302 // no timer click is missed (5302 < 6000)
entering: 6000 // normal execution times can resume
sleeping: 200
leaving: 6201
entering: 7000
sleeping: 200
leaving: 7201
如果错过两次或两次以上的单击,则只会出现问题。执行时间不会与第一次执行同步,而是与暂停完成的时间同步:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 2500
leaving: 4500 // two timer clicks are missed (3000 and 4000)
entering: 4500 // hence, the following execution happens right away
sleeping: 200
leaving: 4701
entering: 5500 // and further execution are also affected...
sleeping: 200
leaving: 5702
entering: 6501
sleeping: 200
leaving: 6702
结论
如果暂停时间可能超过计时器间隔的两倍,则必须使用的解决方案,但在其他情况下,则不需要此解决方案,并且上述简单的实现效果良好。在这种情况下,您希望具有以下行为:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500 // one timer click is missed
entering: 4000 // I don't want t execute the 3th execution
sleeping: 200
leaving: 4200
然后,您仍然可以使用这个简单的实现,只需检查
enteringTime
。如果是真的,拍摄图片,如果是假的,什么都不做。我的直觉反应是考虑将拍摄图片并保存到磁盘的逻辑从主gui线程移动到一个单独的线程中。这应该会使照片线程更容易在常规int上运行
n[2] = 2500; // big stall (more than 2sec)
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 2500
leaving: 4500 // two timer clicks are missed (3000 and 4000)
entering: 4500 // hence, the following execution happens right away
sleeping: 200
leaving: 4701
entering: 5500 // and further execution are also affected...
sleeping: 200
leaving: 5702
entering: 6501
sleeping: 200
leaving: 6702
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500 // one timer click is missed
entering: 4000 // I don't want t execute the 3th execution
sleeping: 200
leaving: 4200