参考自这篇文章:

https://light11.hatenadiary.com/entry/2021/04/19/202107

总结下来,就是我们没有进行异步等待时,产生的异常将被视为开放异常,并且因为没有进行等待这个任务完成,则就无法捕获异步任务中的异常,在这种情况下,当 GC 运行时,它将被视为开放异常,,此时才有可能抛出(实测异常发生后很久untiy也没有捕获到异常日志)

在异步上下文中,使用try-catch捕获

using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class Example : MonoBehaviour
{
    private async void Start()
    {        
        // 例外の内容のエラーログが出力される
        // try-catchでハンドリングできる
        await SomeProcessAsync();
    }

    private async UniTask SomeProcessAsync()
    {
        await UniTask.RunOnThreadPool(() => throw new Exception("ExampleException"));
    }
}

如下情况,将视为开放异常,基本上unity不会捕获到异常信息。

using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class Example : MonoBehaviour
{
    private async void Start()
    {        
        // try-catchできない
        // GCのタイミングで例外が出力される
        SomeProcessAsync().Forget();
    }

    private async UniTask SomeProcessAsync()
    {
        await UniTask.RunOnThreadPool(() => throw new Exception("ExampleException"));
    }
}

解决方案

有两种解决方案,只测试后附加回调,另一种请自行测试。

  • 修改UniTaskScheduler.UnobservedExceptionWriteLogType 日志等级,便于发生异常时直接在unity日志上输出对应等级错误提醒

using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class Example : MonoBehaviour
{
    private async void Start()
    {
        UniTaskScheduler.UnobservedExceptionWriteLogType = LogType.Warning;
        
        // try-catchできない
        SomeProcessAsync().Forget();
    }

    private async UniTask SomeProcessAsync()
    {
        await UniTask.RunOnThreadPool(() => throw new Exception("ExampleException"));
    }
}

现在,将打印一条警告,并显示一条消息, UnobservedTaskException: System.Exception: ExampleException 例如“当发生未处理的异常时”。

f:id:halya_11:20201220174250p:plain

  • 添加注册一个处理回调,这个也是比较推荐的做法

using System;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class Example : MonoBehaviour
{
    private async void Start()
    {
        // 未処理例外のハンドリング処理を書き換える
        UniTaskScheduler.UnobservedTaskException += HandleUnobservedTaskExceptions;
        
        SomeProcessAsync().Forget();
    }

    private async UniTask SomeProcessAsync()
    {
        await UniTask.RunOnThreadPool(() => throw new Exception("ExampleException"));
    }
    
    private static void HandleUnobservedTaskExceptions(Exception ex)
    {
        Debug.LogError("未処理例外発生: " + ex);
    }
}

可以看到回调的内容得到了体现。

同时本方法可以放在.Forget(); 作为参数传递,即.Forget(HandleUnobservedTaskExceptions);