如何实现AVAudioPlayer播放完成后退出函数且不阻塞主线程?(已试ManualResetEvent无效)
解决方案:用TaskCompletionSource异步等待,避免阻塞主线程
你的问题出在两个核心点:一是ManualResetEvent的使用逻辑容易引发线程死锁,二是局部的AVAudioPlayer实例可能被GC提前回收,直接导致FinishedPlaying回调完全不触发。下面是靠谱的实现方案:
核心思路
- 用
TaskCompletionSource<bool>替代ManualResetEvent,通过异步等待的方式避免阻塞主线程 - 保持对
AVAudioPlayer实例的强引用直到播放完成,防止GC提前回收播放器
完整代码示例
public async Task PlayAudioAndWaitAsync(string soundResource) { NSError err; var tcs = new TaskCompletionSource<bool>(); // 保持对播放器的强引用,避免被GC提前回收 var currentPlayer = new AVAudioPlayer(soundResource, "mp3", out err); if (err != null || currentPlayer == null) { tcs.SetException(new Exception($"初始化播放器失败:{err.LocalizedDescription}")); return; } currentPlayer.FinishedPlaying += (sender, e) => { tcs.SetResult(true); // 播放完成后清理引用,允许GC回收资源 currentPlayer.FinishedPlaying -= null; currentPlayer = null; }; currentPlayer.Play(); // 异步等待播放完成,不会阻塞主线程 await tcs.Task; }
原代码失效原因
- GC回收问题:你的
currentPlayer是局部变量,JIT可能判定它后续无引用,提前回收实例,导致FinishedPlaying回调永远不会被触发。 - 线程死锁风险:
DispatchSync会阻塞当前线程等待全局队列任务完成,而AVAudioPlayer的FinishedPlaying回调默认在主线程执行,若主线程被占用,回调无法执行,事件永远不会被Set。
使用说明
调用时使用await关键字即可,比如在UI交互事件中:
await PlayAudioAndWaitAsync("your_sound_file.mp3");
这种异步等待的方式不会阻塞主线程,UI仍能正常响应用户操作。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

