在图形应用程序中使用计时器时系统总冻结 [英] Total system freezing when using timers in graphical application

查看:32
本文介绍了在图形应用程序中使用计时器时系统总冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我真的被这个问题困住了,非常感谢任何建议.

I’m really stuck with this issue and will greatly appreciate any advice.

问题:我们的一些用户在使用我们的产品时抱怨整个系统冻结".无论我们如何尝试,我们都无法在任何可用于故障排除的系统中重现它.

The problem: Some of our users complain about total system "freezing" when using our product. No matter how we tried, we couldn’t reproduce it in any of systems available for troubleshooting.

产品:从物理上讲,它是一个 32 位/64 位 DLL.产品具有自动刷新的图形用户界面,可实时绘制音频信号的频谱图

The product: Physically, it’s a 32bit/64bit DLL. The product has a self-refreshing GUI, which draws a realtime spectrogram of an audio signal

问题详情:我从一些零散的报告中设法收集到的信息如下:

Problem details: What I managed to collect from a number of fragmentary reports makes the following picture:

打开 GIU 时,有时是立即打开,有时在 GIU 可见几分钟后,系统完全停止,无法使用 Windows 进行操作,无法启动任务管理器等.键盘上没有反应,没有看到鼠标光标(或看到但对鼠标移动没有反应——这个我不知道).用户必须硬重置系统才能重新启动.我认为重要的是(在某些情况下)GIU 在一段时间内会做出响应并显示一些足够的图片.然后发生这种冻结.其中一份报告说,一旦系统被冻结,音频就会继续呈现——即记者听到(但 Windows 的整个图形外壳已经被冻结).注意:在这类应用中,它通常是一个专门负责声音处理的线程.

When GIU is opened, sometimes immediately, sometimes after a few minutes of GIU being visible, the system completely stalls, without possibility to operate with windows, start Task Manager etc. No reactions on keyboard, no mouse cursor seen (or it’s seen but is not responsibe to mouse movements – this I do not know). The user has to hard-reset the system in order to reboot. What is important, I think, is that (in some cases) for some time the GIU is responsive and shows some adequate pictures. Then this freezing happens. One of the reports tells that once the system was frozen, the audio continued to be rendered – i.e. heard by the reporter (but the whole graphic shell of Windows was already frozen). Note: in this sort of apps it’s usually a specialized thread which is responsible for sound processing.

使用 32 位和 64 位版本的 DLL 或多或少地确认了 Windows7 x64 上的 2 个用户会发生冻结,从未听说过与此冻结相关的任何其他操作系统(尽管有 1 份报告没有任何操作系统指定).

The freezing is more or less confirmed to happen for 2 users on Windows7 x64 using both 32 and 64 bit versions of the DLL, never heard of any other OSs mentioned with connection to this freezing (though there was 1 report without any OS specified).

这就是我设法收集的全部内容.

That’s all that I managed to collect.

架构/怀疑:

我强烈怀疑是 GUI 刷新周期是罪魁祸首.

I strongly suspect that it’s the GUI refreshing cycle that is a culprit.

基本上,它是这样工作的:

Basically, it works like this:

  1. 有一个定时器可以以大约 25 fps 的帧速率触发回调.
  2. 在此回调中执行音频分析并更新 GUI

关于定时器的一些细节:

Some details about the timer:

基于此调用:

CreateTimerQueueTimer(&m_timerHandle, NULL, xPlatformTimerCallbackWrapper,
       this, m_firstExpInterval, m_period, WT_EXECUTEINTIMERTHREAD);

我们创建一个计时器,并定期调用 m_timerHandle.

We create a timer and m_timerHandle is called periodically.

GUI 刷新的一些细节:

Some details about the GUI refreshing:

它是这样工作的:

HDC hdc = GetDC (hwnd);
// Some drawing
ReleaseDC(hwnd,hdc);

我的直觉告诉我,这个 CreateTimeQueueTimer 可能不是正确的决定.参考页面 说明在使用的情况下WT_EXECUTEINTIMERTHREAD:

My intuition tells me that this CreateTimeQueueTimer might be not the right decision. The reference page tells that in case of using WT_EXECUTEINTIMERTHREAD:

回调函数由定时器线程本身调用.这旗应仅用于短期任务或它可能会影响其他计时器操作.回调函数是作为 APC 排队.它不应该执行警报等待操作.

我不记得为什么实际上选择了这个 WT_EXECUTEINTIMERTHREAD 选项,现在 WT_EXECUTEDEFAULT 似乎同样适合我.

I don’t remember why this WT_EXECUTEINTIMERTHREAD option was chosen actually, now WT_EXECUTEDEFAULT seems equally suitable for me.

事实上,我认为使用参考页面中提到的任何选项都没有任何重大区别.

In fact, I don’t see any major difference in using any of the options mentioned in the reference page.

问题:

  1. 被告知的内容是否能让任何人了解可能出现的问题?
  2. 您是否遇到过类似的问题,原因是什么?

感谢您提供任何信息!

更新:2010-02-20

Update: 2010-02-20

不幸的是,这里给出的建议(到目前为止我可以检查)没有帮助,即:

Unfortunatelly, the advise given here (which I could check so far) didn't help, namelly:

  • CreateTimerQueueTimer(&m_timerHandle,NULL,xPlatformTimerCallbackWrapper,this,m_firstExpInterval,m_period, WT_EXECUTEDEFAULT)中更改为WT_EXECUTEDEFAULT;
  • 可重入守卫已经存在
  • 我还没有检查更新 WM_PAINT 处理程序中的 GUI 是否有帮助

还是谢谢你的提示.

现在,我已经玩了一段时间了,也得到了真正的 W7 安装(我曾经使用虚拟安装),看来问题可以缩小了.

Now, I've been playing with this for a while, also got a real W7 intallation (I used to use the virtual one) and it seems that the problem can be narrowed down.

在我的安装过程中,使用该应用程序确实使 GUI 的响应速度大大降低,尽管我无法像有人报告的那样重现整个系统冻结.

On my installation, using of the app really get the GUI far less responsive, although I couldn't manage to reproduce a total system freezing as someone reported.

我现在的假设是这种响应能力下降和报告的完全冻结具有共同的起源.

My assumption now is this responsiveness degradation and reported total freezing have a common origin.

然后我做了一些原始分析,发现至少有一个罪魁祸首是 BitBlt 函数,该函数每秒被调用约 50 次

Then I did some primitive profiling and found that at least one of the culprits is BitBlt function that is called approx 50 times a second

BitBlt ((HDC)pContext->getSystemContext (),  // hdcDest
                destRect.left + pContext->offset.h, 
                destRect.top + pContext->offset.v,  
                destRect.right - destRect.left,    
                destRect.bottom - destRect.top,    

                (HDC)pSystemContext,                
                srcOffset.h,                    
                srcOffset.v,           
                SRCCOPY);           

被复制的区域并不是很大(大约 400x200 像素).用于显示后台缓冲区,在定时器回调中执行.

The regions being copied are not really large (approx. 400x200 pixels). It is used for displaying the backbuffer and is executed in the timer callback.

如果我注释掉这个 BitBlt 调用,问题似乎会消失(至少部分消失).

If I comment out this BitBlt call, the problem seems to disappear (at least partly).

在运行 WinXP 的同一台机器上一切正常.

On the same machine running WinXP everything works just fine.

对此有什么想法吗?

推荐答案

最有可能的情况是您的计时器回调执行时间超过 25 毫秒.然后另一个计时器滴答出现,it 也开始处理.依此类推,很快你就会有一大堆线程占用 CPU 周期,所有线程都在尝试进行音频分析,并且在短时间内系统忙于进行线程上下文切换,以至于没有真正的工作完成.同时,越来越多的计时器滴答被放入队列中.

Most likely what's happening is that your timer callback is taking more than 25 ms to execute. Then another timer tick comes along and it starts processing, too. And so on, and pretty soon you have a whole bunch of threads sucking down CPU cycles, all trying to do your audio analysis and in short order the system is so busy doing thread context switches that no real work gets done. And all the while, more and more timer ticks are getting placed into the queue.

强烈建议您在此处使用 WT_EXECUTEDEFAULT,而不是 WT_EXECUTEINTIMERTHREAD.此外,您需要防止重叠计时器回调.有几种方法可以做到这一点.

I would strongly suggest that you use WT_EXECUTEDEFAULT here, rather than WT_EXECUTEINTIMERTHREAD. Also, you need to prevent overlapping timer callbacks. There are several ways to do that.

您可以在计时器回调中使用临界区.当回调被触发时,它会调用 TryEnterEnterCriticalSection,如果不成功,则不做任何事情就返回.

You can use a critical section in your timer callback. When the callback is triggered it calls TryEnterEnterCriticalSection and if not successful, just returns without doing anything.

您可以使用 volatile 变量和 InterlockedCompareExchange 来做类似的事情.

You can do something similar using a volatile variable and InterlockedCompareExchange.

或者,您可以将计时器更改为一次性 (WT_EXECUTEONLYONCE),然后在每次回调结束时重新设置计时器.这将使该事物在最后一个完成后 25 毫秒执行.

Or, you can change your timer to be a one-shot (WT_EXECUTEONLYONCE), and then re-set the timer at the end of every callback. That would make the thing execute 25 ms after the last one completed.

您选择哪个取决于您.如果您的分析通常需要超过 25 毫秒但不超过 35 毫秒,那么您可能会使用 WT_EXECUTEONLYONCE 获得更平滑的更新率.如果分析时间很少超过 25 毫秒,或者通常需要超过 35 毫秒(但少于 50 毫秒),那么您最好使用其他技术之一.

Which you choose is up to you. If your analysis often takes longer than 25 ms but not more than 35 ms, then you'll probably get a smoother update rate using WT_EXECUTEONLYONCE. If it's rare that analysis takes more than 25 ms, or if it often takes more than about 35 ms (but less than 50 ms), then you're probably better off using one of the other techniques.

当然,如果经常需要超过 25 ms,那么你可能想增加时间(降低更新速率).

Of course, if it often takes longer than 25 ms, then you probably want to increase the time (reduce the update rate).

此外,正如其中一位评论者指出的那样,该问题也可能涉及从计时器线程访问 GUI.您应该在计时器线程中进行所有分析,将结果存储在主线程可以访问的地方,然后向窗口过程发送消息,告诉它更新显示.

Also, as one of the commenters pointed out, it's possible that the problem also involves accessing the GUI from the timer thread. You should do all of your analysis in the timer thread, store the results somewhere that the main thread can access it, and then send a message to the window proc, telling it to update the display.

这篇关于在图形应用程序中使用计时器时系统总冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆