.NET 7中NAudio WaveIn录音偶发AccessViolationException问题排查
.NET7下NAudio WaveIn偶发内存访问冲突的解决思路
异常根源分析
这个System.AccessViolationException本质是多线程环境下,对NAudio底层Windows音频API的内存资源(比如WaveHeader结构体)操作不同步导致的——当多个线程同时触发录音启停,或者实例资源未正确释放就被重复操作时,会出现读写受保护内存的情况,尤其是长期运行的应用,资源泄漏或并发冲突积累到一定程度就会崩溃。
核心问题解答:加锁管理启停是有效的
通过加锁同步所有对WaveIn实例的操作,能从根本上避免多线程并发操作导致的资源冲突,是解决这类问题的关键手段之一,但要注意锁的正确使用方式和资源生命周期管理。
具体解决方案
给WaveIn操作加全局锁
定义一个专用锁对象,包裹所有涉及WaveIn实例的创建、启动、停止、释放操作,确保同一时间只有一个线程能操作实例:private readonly object _waveInLock = new object(); private WaveInEvent? _pcmWaveIn; // 启动录音逻辑 public void StartRecording() { lock (_waveInLock) { if (_pcmWaveIn == null) { Console.WriteLine("PcmWaveIn init started..."); _pcmWaveIn = new WaveInEvent { DeviceNumber = 0, WaveFormat = new WaveFormat(8000, 16, 1), BufferMilliseconds = 20 }; _pcmWaveIn.DataAvailable += OnDataAvailable; _pcmWaveIn.RecordingStopped += (s, e) => { // 停止后及时清理资源 _pcmWaveIn?.Dispose(); _pcmWaveIn = null; }; Console.WriteLine("PcmWaveIn init complete..."); } _pcmWaveIn.StartRecording(); } } // 停止录音逻辑 public void StopRecording() { lock (_waveInLock) { _pcmWaveIn?.StopRecording(); // 这里不需要立即Dispose,交给RecordingStopped事件处理更安全 } }优化DataAvailable事件处理,避免阻塞录音线程
录音线程是底层音频API的回调线程,一旦被阻塞会导致缓冲区积压,进而引发内存操作异常。把耗时的编码、业务逻辑放到独立线程处理:private void OnDataAvailable(object? sender, WaveInEventArgs e) { // 先复制缓冲区,避免原缓冲区被底层覆盖 byte[] bufferCopy = new byte[e.BytesRecorded]; Array.Copy(e.Buffer, bufferCopy, e.BytesRecorded); // 用Task.Run异步处理编码和业务逻辑,不阻塞录音线程 _ = Task.Run(() => { Int16[] values = new Int16[bufferCopy.Length / 2]; Buffer.BlockCopy(bufferCopy, 0, values, 0, bufferCopy.Length); byte[] alawData = new byte[bufferCopy.Length / 2]; for (int i = 0; i < bufferCopy.Length; i += 2) { short sample = BitConverter.ToInt16(bufferCopy, i); alawData[i / 2] = NAudio.Codecs.ALawEncoder.LinearToALawSample(sample); } // 后续业务逻辑... }); }严格管理资源生命周期
长期运行的应用中,不要让WaveIn实例长期驻留内存:- 在
RecordingStopped事件中调用Dispose()释放底层API资源,避免资源泄漏 - 每次停止录音后将实例置为null,下次启动重新创建,减少长期运行的资源累积风险
- 在
额外注意事项
- 禁止任何无锁的多线程直接操作WaveIn实例,包括读取实例状态、调用方法
- 检查应用中是否有其他线程意外修改
_pcmWaveIn变量(比如置null),锁能有效避免这类非法操作 - 对于高并发场景,可以考虑用状态机(比如记录Idle/Recording/Stopping状态)配合锁,进一步避免无效操作
内容的提问来源于stack exchange,提问作者Majico
相关产品推荐
相关产品推荐

