Open2

HatopopoMemo

hatopopohatopopo

UniTaskのCancellationTokenSourceでCancelする際の処理

asyncメソッド実行時にCancellationTokenをリレーしていくが、具体どこでOperationCanceledException が吐かれるのかを改めて調べた。

結論 : Cancel処理が行われた、次の await が呼ばれる瞬間

この結論さえ覚えておけばエラーハンドリングも容易にできそう。

実験

    private CancellationTokenSource _cts;

    private async void Start()
    {
        _cts = new CancellationTokenSource();

        Debug.Log($"{nameof(TokenTest)}.{nameof(Start)}");
        await AsyncMethod(_cts.Token);
        Debug.Log($"end {nameof(AsyncMethod)}");
        await UniTask.Delay(1, cancellationToken: _cts.Token);
        Debug.Log("Hello,,,");
    }

    private async UniTask AsyncMethod(CancellationToken token)
    {
        Debug.Log($"{nameof(AsyncMethod)}");
        await UniTask.Delay(1, cancellationToken: token);
        Debug.Log("Cancel");
        _cts.Cancel();
        Debug.Log("Canceled");
    }

この場合のログは AsyncMethod が完了した際の end AsyncMethod までのログが出て、OperationCanceledException が呼ばれた。
よって、CancellationTokenSource.Cancel() が呼ばれたタイミングでは同期メソッドのキャンセルはされずに、次のawaitまでは実行されることがわかる。

hatopopohatopopo

C# Lazy<T> について

遅延評価を行うことができるクラス

Lazy<T>()

using System;

Lazy<string> instance = new(() => "Lazy");

Console.WriteLine(instance.IsValueCreated);  // false
Console.WriteLine(instance.Value);  // Lazy
Console.WriteLine(instance.IsValueCreated); // true

初めて値へアクセスする際にインスタンスが作られる = コンストラクタが走る

Lazy<T>(Func<T> valueFactory)

  • 初期化時に呼ばれるメソッドを登録することができる = factoryMethod
  • ここで new T(args) をしてねってこと。
  • 要は遅延評価
  • 重たいインスタンスだが、親インスタンスと同時に作成する必要がなければこっちで作るでもアリかも。

つまり、

// Field
HogeClass instance { get; set; }

// Method
if(instance == null){
    instance = new HogeClass();
}
instance.Value;

これとか、

public HogeClass Instance => _instance;
private HogeClass _instance ??= new HogeClass();

これが必要なくなる


ちょっと応用
Lazy<T>(bool isThreadSafe)

  • スレッドセーフで作ることができる
  • 非同期を多用しているならtrueのが良き

Lazy<T>(LazyThreadSafetyMode mode)

  • 初期化時のモード指定
    • None
      • デフォルト
      • 軽い
    • PublicationOnly
      • 初期化メソッドは各スレッドで実行されるが有効なのは最初にインスタンス生成が行えたもののみ。
      • 実質的なスレッドセーフ
    • ExecutionAndPublication
      • 完全なスレッドセーフ
      • isThreadSafetrue に指定することで内部的にこのパラメータが指定される
      • デッドロックに注意

単一の値として扱うなら isThreadSafetrue にして運用するのがベターか。
スレッド考慮しなくても良いなら何も指定しない方が早い。


次は System.Threading.LazyInitializer について調べてみる