-
关于使用NAudio麦克风扬声器组件造成WPF应用程序卡死问题跟踪及异步队列的实现
由于WPF应用程序出现卡死的情况,特记录一下问题的跟踪情况
1、多次进行NAudio事件注册,没有启用注销再注册的方式,造成应用程序CPU过高
private AudioNotificationClient audioNotification = new AudioNotificationClient();
audioNotification.DeviceStateChanged += AudioNotification_DeviceStateChanged;
private MMDeviceEnumerator _mmDeviceEnumerator = new MMDeviceEnumerator();
_mmDeviceEnumerator.RegisterEndpointNotificationCallback(audioNotification);
缺少注销
_mmDeviceEnumerator.UnregisterEndpointNotificationCallback(audioNotification);
2、事件注册同时麦克风设备状态发生改变DeviceStateChanged,造成线程死锁
可以使用异步队列,把事件的注册,注销与DeviceStateChanged执行逻辑都放进异步队列,保证不会出现同时执行的情况。
异步队列的实现:
/// <summary> /// 异步任务队列 /// </summary> public class AsyncTaskQueue : IDisposable { private bool _isDisposed; private readonly ConcurrentQueue<AwaitableTask> _queue = new ConcurrentQueue<AwaitableTask>(); private Thread _thread; private AutoResetEvent _autoResetEvent; /// <summary> /// 异步任务队列 /// </summary> public AsyncTaskQueue() { _autoResetEvent = new AutoResetEvent(false); _thread = new Thread(InternalRuning) {IsBackground = true}; _thread.Start(); } private bool TryGetNextTask(out AwaitableTask task) { task = null; while (_queue.Count > 0) { if (_queue.TryDequeue(out task) && (!AutoCancelPreviousTask || _queue.Count == 0)) return true; task.Cancel(); } return false; } private AwaitableTask PenddingTask(AwaitableTask task) { lock (_queue) { Debug.Assert(task != null); _queue.Enqueue(task); _autoResetEvent.Set(); } return task; } private void InternalRuning() { while (!_isDisposed) { if (_queue.Count == 0) { _autoResetEvent.WaitOne(); } while (TryGetNextTask(out var task)) { if (task.IsCancel) continue; if (UseSingleThread) { task.RunSynchronously(); } else { task.Start(); } } } } /// <summary> /// 是否使用单线程完成任务. /// </summary> public bool UseSingleThread { get; set; } = true; /// <summary> /// 自动取消以前的任务。 /// </summary> public bool AutoCancelPreviousTask { get; set; } = false; /// <summary> /// 执行任务 /// </summary> /// <param name="action"></param> /// <returns></returns> public AwaitableTask Run(Action action) => PenddingTask(new AwaitableTask(new Task(action, new CancellationToken(false)))); /// <summary> /// 执行任务 /// </summary> /// <typeparam name="TResult"></typeparam> /// <param name="function"></param> /// <returns></returns> public AwaitableTask<TResult> Run<TResult>(Func<TResult> function) => (AwaitableTask<TResult>) PenddingTask(new AwaitableTask<TResult>(new Task<TResult>(function))); /// <inheritdoc /> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// 析构任务队列 /// </summary> ~AsyncTaskQueue() => Dispose(false); private void Dispose(bool disposing) { if (_isDisposed) return; if (disposing) { _autoResetEvent.Dispose(); } _thread = null; _autoResetEvent = null; _isDisposed = true; } /// <summary> /// 可等待的任务 /// </summary> public class AwaitableTask { private readonly Task _task; /// <summary> /// 初始化可等待的任务。 /// </summary> /// <param name="task"></param> public AwaitableTask(Task task) => _task = task; /// <summary> /// 任务的Id /// </summary> public int TaskId => _task.Id; /// <summary> /// 任务是否取消 /// </summary> public bool IsCancel { get; private set; } /// <summary> /// 开始任务 /// </summary> public void Start() => _task.Start(); /// <summary> /// 同步执行开始任务 /// </summary> public void RunSynchronously() => _task.RunSynchronously(); /// <summary> /// 取消任务 /// </summary> public void Cancel() => IsCancel = true; /// <summary> /// 获取任务等待器 /// </summary> /// <returns></returns> public TaskAwaiter GetAwaiter() => new TaskAwaiter(this); /// <summary>Provides an object that waits for the completion of an asynchronous task. </summary> [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)] public struct TaskAwaiter : INotifyCompletion { private readonly AwaitableTask _task; /// <summary> /// 任务等待器 /// </summary> /// <param name="awaitableTask"></param> public TaskAwaiter(AwaitableTask awaitableTask) => _task = awaitableTask; /// <summary> /// 任务是否完成. /// </summary> public bool IsCompleted => _task._task.IsCompleted; /// <inheritdoc /> public void OnCompleted(Action continuation) { var This = this; _task._task.ContinueWith(t => { if (!This._task.IsCancel) continuation?.Invoke(); }); } /// <summary> /// 获取任务结果 /// </summary> public void GetResult() => _task._task.Wait(); } } /// <summary> /// 可等待的任务 /// </summary> /// <typeparam name="TResult"></typeparam> public class AwaitableTask<TResult> : AwaitableTask { /// <summary> /// 初始化可等待的任务 /// </summary> /// <param name="task">需要执行的任务</param> public AwaitableTask(Task<TResult> task) : base(task) => _task = task; private readonly Task<TResult> _task; /// <summary> /// 获取任务等待器 /// </summary> /// <returns></returns> public new TaskAwaiter GetAwaiter() => new TaskAwaiter(this); /// <summary> /// 任务等待器 /// </summary> [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)] public new struct TaskAwaiter : INotifyCompletion { private readonly AwaitableTask<TResult> _task; /// <summary> /// 初始化任务等待器 /// </summary> /// <param name="awaitableTask"></param> public TaskAwaiter(AwaitableTask<TResult> awaitableTask) => _task = awaitableTask; /// <summary> /// 任务是否已完成。 /// </summary> public bool IsCompleted => _task._task.IsCompleted; /// <inheritdoc /> public void OnCompleted(Action continuation) { var This = this; _task._task.ContinueWith(t => { if (!This._task.IsCancel) continuation?.Invoke(); }); } /// <summary> /// 获取任务结果。 /// </summary> /// <returns></returns> public TResult GetResult() => _task._task.Result; } }
出处:https://www.cnblogs.com/log9527blog/p/16517315.html
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数