【Unity】UniTaskの例外処理まとめ

UniTaskの例外処理についてまとめました。

Unity2020.1.10
UniTask 2.0.37

非同期タスクを待機する場合

まず非同期タスクをawaitで待機する場合には同期メソッドと同じようにtry-catchでハンドリングできます。
またUnityのConsoleにエラーログとして例外の内容が出力されます。

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"));
    }
}

このあたりは普通のTaskと同様の挙動です。

非同期タスクを待機しないと未処理例外として処理される

一方で、非同期タスクを待機しない場合にはtry-catchできません。
この場合にはGCが走ったタイミングで未処理例外として処理されます。
(このバージョンの)UniTaskでは未処理例外のデフォルトの挙動として、例外がスローされます。

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を変更することで未処理例外のログ出力方法を変更できます。

以下では未処理例外発生時にWarningが発生するようにしています。

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
警告

未処理例外のハンドリング処理を書き換える

またUniTaskScheduler.UnobservedTaskExceptionで未処理例外のハンドリング処理を上書きすることもできます。

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);
    }
}

再生すると書き換えた内容が反映されていることが確認できます。

f:id:halya_11:20201220174652p:plain
書き換え後

関連

light11.hatenadiary.com