MRが楽しい

MRやVRについて学習したことを書き残す

C#でSemaphoreSlimとCancellationTokenSourceを使って非同期タスクの制御を行う

本日は C# の小ネタ枠です。
C# で SemaphoreSlim と CancellationTokenSource を使って非同期タスクの制御を行う一例です。

前回記事

以下の記事でスレッドが一定時間待機すると待機をキャンセルする実装を行いました。
bluebirdofoz.hatenablog.com

本記事では後から新しい処理が呼び出されたとき、現在のスレッドをキャンセルして次の処理を実行してみます。

SemaphoreSlim.CurrentCount

現在 SemaphoreSlim で処理が実行されているかスレッドに空きがあるかは SemaphoreSlim.CurrentCount で確認できます。
現在利用可能なスレッド数が返却されます。
learn.microsoft.com

CancellationTokenSource.Cancel

CancellationTokenSource.Cancel を使って Cancel を任意のタイミングで発生させます。
learn.microsoft.com

サンプルシーン

新しい処理が呼び出されたとき、既に実行中の処理をキャンセルするように以下の通り実装を変更しました。
・SemaphoreSlimCancelTest2.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class SemaphoreSlimCancelTest2 : MonoBehaviour
{ 
    // 同時に1つまでのタスクを許容し、かつ、タスクの数を後から追加できないようにする
    private SemaphoreSlim _semaphoreSlim = new (1,1);
    
    private CancellationTokenSource _cancelTokenSource;
    
    public async void ButtonEvent()
    {
        // 既にタスクが実行されていれば処理をキャンセルする
        if (_semaphoreSlim.CurrentCount == 0) _cancelTokenSource.Cancel();
        try
        {
            // スレッドをブロックする(非同期で行いたい場合は WaitAsync() を使用する)
            await _semaphoreSlim.WaitAsync();
            _cancelTokenSource = new();
            // 1秒ごとに1~3のカウントダウンをログに出力する
            for (int count = 1; count <= 3; count++)
            {
                Debug.Log(count);
                await Task.Delay(1000, _cancelTokenSource.Token);
            }
        }
        catch (OperationCanceledException)
        {
            Debug.Log("待機キャンセルされました");
        }
        finally
        {
            // スレッドを解放する
            _semaphoreSlim.Release();
        }
    }
}

シーンを再生して動作を確認します。
ボタンを連打すると、以下の通り2回目の処理が割り込んだときにその前の処理がキャンセルされました。