我真的很困惑这个问题,非常感谢任何建议。
问题: 我们的一些用户抱怨使用我们的产品时整个系统“冻结”。无论我们如何尝试,我们都无法在任何可用于故障排除的系统中重现它。
产品 从物理上讲,它是一个32位/ 64位DLL。该产品具有自刷新的GUI,可绘制音频信号的实时频谱图
问题详情: 我从一些零碎的报告中收集到的信息如下:
当GIU打开时,有时会立即打开,有时候在看到几分钟的GIU后,系统会完全失速,无法操作窗口,启动任务管理器等。键盘没有反应,没有看到鼠标光标(或者是看到但不负责鼠标移动 - 这我不知道。用户必须硬重置系统才能重新启动。我认为重要的是,在某些情况下(在某些情况下)GIU具有响应性,并显示一些足够的图片。然后这种冻结发生了。其中一个报告告诉我们,一旦系统被冻结,音频将继续呈现 - 即由记者听到(但Windows的整个图形外壳已经被冻结)。注意:在这种应用程序中,它通常是一个负责声音处理的专用线程。
对于使用32位和64位版本的DLL的Windows7 x64上的2个用户,或多或少确认会发生冻结,从未听说过与此冻结相关的任何其他操作系统(尽管有1个报告没有任何操作系统)指定)。
这就是我设法收集的全部内容。
架构/怀疑:
我强烈怀疑这是GUI刷新周期的罪魁祸首。
基本上,它的工作原理如下:
有关计时器的一些细节:
这是基于这个电话:
CreateTimerQueueTimer(&m_timerHandle, NULL, xPlatformTimerCallbackWrapper,
this, m_firstExpInterval, m_period, WT_EXECUTEINTIMERTHREAD);
我们创建一个计时器,并定期调用m_timerHandle
。
有关GUI刷新的一些细节:
它的工作原理如下:
HDC hdc = GetDC (hwnd);
// Some drawing
ReleaseDC(hwnd,hdc);
我的直觉告诉我,这CreateTimeQueueTimer
可能不是正确的决定。 reference page告诉我使用WT_EXECUTEINTIMERTHREAD
:
回调函数由计时器线程本身调用。这面旗帜 应该只用于短期任务或 它可能影响其他计时器 操作。回调函数是 排队作为APC。它不应该 执行可警告的等待操作。
我不记得为什么实际选择了这个WT_EXECUTEINTIMERTHREAD
选项,现在WT_EXECUTEDEFAULT
似乎同样适合我。
事实上,我没有看到使用参考页面中提到的任何选项有任何重大差异。
问题:
感谢您的任何信息!
更新:2010-02-20
不幸的是,这里给出的建议(到目前为止我可以检查)没有帮助,名字:
CreateTimerQueueTimer(&m_timerHandle,NULL,xPlatformTimerCallbackWrapper,this,m_firstExpInterval,m_period, WT_EXECUTEDEFAULT);
无论如何,谢谢你的提示。
现在,我已经玩了一段时间,还有一个真正的W7安装(我以前使用虚拟的),似乎问题可以缩小。
在我的安装中,使用应用程序确实让GUI响应性降低,尽管我无法像某人报告的那样设法重现整个系统的冻结。
我现在的假设是这种反应性降低和报告的总冻结有一个共同的起源。
然后我做了一些原始的分析,发现至少有一个罪魁祸首是 BitBlt 函数,每秒调用约50次
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像素)。它用于显示后备缓冲区,并在定时器回调中执行。
如果我注释掉这个BitBlt调用,问题似乎消失了(至少部分)。
在运行WinXP的同一台机器上,一切正常。
有关于此的任何想法吗?
答案 0 :(得分:2)
最有可能发生的事情是你的计时器回调执行时间超过25毫秒。然后另一个计时器滴答,它也开始处理。等等,很快就会有一大堆线程吸收CPU周期,所有人都试图进行音频分析,而且在短时间内系统忙于进行线程上下文切换,没有真正的工作完成。而且一直以来,越来越多的计时器滴答被放入队列中。
我强烈建议你在这里使用WT_EXECUTEDEFAULT
,而不是WT_EXECUTEINTIMERTHREAD
。此外,您需要防止重叠的计时器回调。有几种方法可以做到这一点。
您可以在计时器回调中使用关键部分。当回调被触发时,它会调用TryEnterEnterCriticalSection
,如果不成功,则只返回而不做任何事情。
您可以使用volatile
变量和InterlockedCompareExchange
来执行类似操作。
或者,您可以将计时器更改为一次性(WT_EXECUTEONLYONCE
),然后在每次回调结束时重新设置计时器。这将使事情在最后一次完成后执行25 ms。
您选择的是由您自己决定的。如果您的分析通常需要超过25毫秒但不超过35毫秒,那么您可能会使用WT_EXECUTEONLYONCE
获得更平滑的更新速率。如果分析时间超过25毫秒,或者通常需要超过35毫秒(但小于50毫秒),那么您最好使用其他技术之一。
当然,如果它通常需要超过25毫秒,那么您可能希望增加时间(降低更新速率)。
另外,正如其中一位评论者指出的那样,问题还可能涉及从计时器线程访问GUI。您应该在计时器线程中进行所有分析,将结果存储在主线程可以访问它的位置,然后向窗口proc发送消息,告诉它更新显示。
答案 1 :(得分:0)
您是否曾要求用户停用Aero / WDM DWM?启用Aero后,渲染实现完全不同。没有Aero,行为将类似于XP。并不是说它能解决任何问题,但它会让你知道问题是什么。