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

如何实现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;
}

原代码失效原因

  1. GC回收问题:你的currentPlayer是局部变量,JIT可能判定它后续无引用,提前回收实例,导致FinishedPlaying回调永远不会被触发。
  2. 线程死锁风险:DispatchSync会阻塞当前线程等待全局队列任务完成,而AVAudioPlayer的FinishedPlaying回调默认在主线程执行,若主线程被占用,回调无法执行,事件永远不会被Set。

使用说明

调用时使用await关键字即可,比如在UI交互事件中:

await PlayAudioAndWaitAsync("your_sound_file.mp3");

这种异步等待的方式不会阻塞主线程,UI仍能正常响应用户操作。

内容的提问来源于stack exchange,提问作者anon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 00:31:14