waveOutReset的N种死法, 及其解决方案

简介:

我遭遇到了调用waveOutReset死锁的问题,在GOOGLE上一搜,遇到同样问题的人还真不少,但没有人很明确地找到造成DEADLOCK的原因,都是糊里糊涂就把问题解决了,然后把运行OK的代码一贴完事。我花了四五个小时才彻底摸清楚规律,把这经验拿出来共享

原则:
(1) waveOutReset不是立即返回的函数, 而需要等待驻留在WAVEDEV里的音频BUFFER全部标记为WOM_DONE,经过callback proc处理完毕后, waveOutReset才会返回. 
(2) 在waveOutReset调用到返回之间的这段时间内, 禁止任何线程对处于reset中的HWAVEOUT调用任何WAVE API, 否则立刻造成死锁.

把握住这两个原则就可以根治waveOutReset死锁问题了. 不过为了把事情描述得更具体些, 我把网上多数人造成死锁的情况列一下, 并给出解决方法

第一种死法:
在回调处理函数WaveCallbackFunc中,对标记为WOM_DONE的BUFFER进行waveOutUnprepareHeader

错误分析:
调用waveOutReset后, 系统内所有未播放的buffer全部被标记为WOM_DONE返回给WaveCallbackFunc, 这属于waveOurReset处理过程的一部分, 此时waveOurReset还未返回. 所以这时如果调用到waveOutUnprepareHeader, 就会立刻造成死锁. 我自己也是犯的这个错误。

解决方法:
在调用waveOurReset之前设置一个FLAG, 然后在WaveCallbackFunc里要调用waveOutUnprepareHeader之前判断一下这个FLAG, 如果处于reset过程中就跳过waveOutUnprepareHeader. 范例代码:

static CRITICAL_SECTION g_CS;
static BOOL g_bResetting = FALSE;

void Reset()
{
        EnterCriticalSection(&g_CS);  //之前InitializeCriticalSection一下
        g_bResetting = TRUE;
        LeaveCriticalSection(&g_CS);
        
        waveOutReset();

        g_bResetting = FALSE;  //RESET后就不会调WaveCallbackFunc了,所以不需要锁
}

VOID CALLBACK WaveCallbackFunc(HWAVEOUT hwo, UINT32 uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
       EnterCriticalSection(&g_CS);   //防止g_bResetting在if判断之后, waveOutUnprepareHeader之前被设成1了, callback和reset处于不同线程
        
        if( (uMsg == WOM_DONE) && !g_bResetting )
        {
                waveOutUnprepareHeader(hWaveOut, pWaveHdr, sizeof(WAVEHDR));
        }
        
        LeaveCriticalSection(&g_CS);
}
        
另外, 如果使用了每个BUFFER定长的BUFFER-CHAIN来做生产者-消费者模型, 那么在BUFFER-CHAIN初始化的时候顺便把所有WAVEHDR都PREPARE好, 而只在BUFFER-CHAIN全部释放的时候才做UNPREPARE, 那么在每次waveOutWrite和WOM_DONE的循环里就不需要PREPARE/UNPREPARE了,这也能避开上述情况造成的死锁


第二种死法: 某些人喜欢在在回调处理函数WaveCallbackFunc中,调用waveOutWrite往系统里填充新BUFFER.
错误分析: 同情况一, 在waveOutReset过程中,如果WaveCallbackFunc调用了waveOutWrite, 同样会造成死锁.
解决方法: 同情况一, 设置FLAG和临界区配合来防止. 不过比较建议在另外的线程中调waveOutWrite写入BUFFER, 这才符合生产-消费模型.

第三种死法: 在另外一个线程中调用waveOurWrite, 但是waveOutReset之前, 没有停下那个线程
错误分析: RESET过程中,还有waveOurWrite调用
解决方法: 把waveOurWrite那个线程suspend或退出, 或者在waveOutWrite前面用FLAG+临界区判断

第四种死法: 也是最匪夷所思的死法, 从UI的某个按钮一路调用到waveOutReset(处于同线程中),  而在WaveCallbackFunc中,同线程调用SetWindowText之类UI函数
错误分析:  事实是:
(1) 如果是在WindowProc的某个CASE里调用SetWindowText, 调用者和SetWindowText处于同线程中,那么运行正常, 多数UI也都会这么写;
(2) 如果处于Thread1的UI调用了waveOutReset, 导致处于Thread2中的WaveCallbackFunc运行并调用SetWindowText, 试图设置处于Thread1的UIcallbackProc, 那么SetWindowText会死锁, 导致WaveCallbackFunc死掉, 最后表面上看起来就是waveOutReset死掉.其实我也不明白为什么这样也能死,要问就去问盖茨大叔吧.
解决方法:
在WaveCallbackFunc中,用RESET FLAG + 临界区的方法保护UI调用函数, 使其在waveOutReset过程中不被调用, 或者用PostMessage这样的异步函数,实在不得不同步影响UI的话就忍一忍等waveOurReset函数返回后再做吧.


本文转自Walzer博客园博客,原文链接:http://www.cnblogs.com/walzer/archive/2007/12/17/1002800.html,如需转载请自行联系原作者


相关文章
|
25天前
|
前端开发 安全 JavaScript
有哪些常见的前端问题和解决方案
【4月更文挑战第13天】前端开发常见问题及解决方案:页面渲染性能优化(减少重绘、回流,利用GPU加速,代码拆分)、响应式设计(媒体查询、弹性布局)、浏览器兼容性(使用前缀,兼容性库,浏览器嗅探)、事件处理(事件委托、防抖节流)、代码组织(模块化、构建工具)、安全性(输入验证、HTTPS、安全HTTP头)和资源加载(CDN、资源优化、错误处理)。
21 6
|
3月前
|
存储 运维 数据库
不敢书面化的解决方案就不是好方案
不敢书面化的解决方案就不是好方案
|
Java Spring
解决方案 --[restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. ERROR 9680 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter
|
9月前
|
存储 编解码 监控
报告厅解决方案
大型单位、企业都建有报告厅,单位搭建报告厅的目的是为了召开各类会议、学术讨论、演讲、报告、新闻发布、多媒体教学培训、观看电影等活动提供卓越的音质效果、清晰的画面显示以及简单便捷的集中控制。报告厅通常会涉及举办活动时将现场画面的保存记录、活动画面以直播的形式在报告厅之外的其他屏幕上观看(因为报告厅容纳人数有限的原因)。针对这两个需求我们做了以下解决方案
开发中遇到的问题&解决方案(四)
今天继续hutool工具类的认识,废话不多说下面直接进入主题。
257 0
开发中遇到的问题&解决方案(四)
|
Java Linux 数据安全/隐私保护
开发中遇到的问题&解决方案(六)
之前的项目里有用到FTP,需要使用FTP把图片及一些必要文件上传到第三方的FTP服务器上,所以会涉及FTP连接,创建用户,创建文件夹,文件上传等,项目里选用的还是hutool包装的FTP工具类,下面来看看这些功能的在hutool里是怎么包装的。
465 0
开发中遇到的问题&解决方案(六)
|
Java 数据库 微服务
开发中遇到的问题&解决方案(三)
我们在日常的开发中会使用到很多工具类,比如最常用的Http请求的,或者日期工具类,处理时间差或者对日期进行特殊处理的,还有就是图片上传类的,比如阿里云或者腾讯云得SSO图片上传,以及Excel文件的导入导出。那这些工具类基本上都是跟着项目的,就是每个项目都写各自的工具类,如果分工明确细化到小组那重复代码就多了,那把工具类集成成一个项目类呢,由一个人去维护这个工具类工程,如果是微服务项目就是一个工具类的微服务,这样做的好处就很明显了,至少公司的研发部在使用到常用的工具类时不必再去网上找到。
249 0
开发中遇到的问题&解决方案(三)
|
SQL NoSQL Oracle
开发中遇到的问题&解决方案(二)
一个软件系统最重要的是什么?毫无疑问是数据,那什么是数据的'运载体',什么作为后端与数据库的桥梁和沟通者呢,是SQL。我是Java开发出身,大学课程当时学习的是SQL Server,而且后面工作中实际用到过Oracle,DB2以及使用的最普遍的My SQL,至于No SQL 这一类下的常用的就是Redis和MongoDB了,这些后面再详细举例说明。今天讲的案列与表情相关,那么接下来进入今天的主题。
137 0
开发中遇到的问题&解决方案(二)
|
Java
开发中遇到的问题&解决方案(八)
以前的一个老项目里使用过ZXing进行会员和门店二维码的生成操作,当时出现过Java版本升级后不兼容的问题,很是麻烦,于是替换成了hutool里的二维码生成,下面我们来看看具体的实现。
177 0
开发中遇到的问题&解决方案(八)
|
人工智能 并行计算 算法
化工行业解决方案
针对化工产业的发展现状和问题,必须把绿色发展、责任关怀、包容性发 展等作为重点,整体推进全行业的转型升级和可持续发展。数字化将成为 这一转型过程的关键推动力。
化工行业解决方案