UnityでTaskの未処理例外をUnobservedTaskExceptionで処理する方法をまとめました。
Unity2020.1.10
非同期タスクを待機する場合は問題なし
まず非同期タスクをawaitで待機する場合には同期メソッドと同じようにtry-catchでハンドリングできます。
またUnityのConsoleにエラーログとして例外の内容が出力されます。
using System; using System.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { private async void Start() { // 例外の内容のエラーログが出力される // try-catchでハンドリングできる await SomeProcessAsync(); } private async Task SomeProcessAsync() { await Task.Run(() => throw new Exception("ExampleException")).ConfigureAwait(false); } }
Wait()やResultした時も同様です。
SomeProcessAsync().Wait();
非同期タスクを待機しないと未処理例外が発生する
一方で、非同期タスクを待機しない場合にはtry-catchできません。
またエラーログも出力されず、このタスク内でスローされた例外は未処理例外となってしまいます。
using System; using System.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { private async void Start() { // エラーログが出力されない // try-catchもできない var _ = SomeProcessAsync(); } private async Task SomeProcessAsync() { await Task.Run(() => throw new Exception("ExampleException")).ConfigureAwait(false); } }
未処理の例外をハンドリングする
このような未処理例外をハンドリングするにはTaskScheduler.UnobservedTaskException
にコールバックを登録します。
するとGCが発生したタイミングで未処理例外がこのコールバックに渡されます。
using System; using System.Threading; using System.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { private async void Start() { TaskScheduler.UnobservedTaskException += HandleUnobservedTaskExceptions; var _ = SomeProcessAsync(); // タスク完了待ち Thread.Sleep(100); // GC発動 GC.Collect(); } private async Task SomeProcessAsync() { await Task.Run(() => throw new Exception("ExampleException")).ConfigureAwait(false); } private static void HandleUnobservedTaskExceptions(object sender, UnobservedTaskExceptionEventArgs e) { foreach (var innerEx in e.Exception.Flatten().InnerExceptions) { Debug.LogError(innerEx); throw innerEx; } // 処理済みとしてマークする e.SetObserved(); } }
これでTaskの未処理例外がエラーログとして出力されるようになりました。