You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:55:44