本日はC#の小ネタ枠です。
C#でSemaphoreSlimを使ってスレッド制御を行う方法についてです。
前回記事
以下の前回記事の続きです。
bluebirdofoz.hatenablog.com
今回はSemaphoreSlimを使ったラッパークラスを作成することで待機キューは1つまでとし、2つ目以降は即キャンセルする制御を行ってみました。
ラッパークラス
SemaphoreSlimを使った以下のラッパークラスを作成しました。
TryEnterAsyncメソッドでセマフォの取得を行い、1つまでは指定時間の待機を許可します。
・LimitedSemaphore.cs
using System.Threading; using System.Threading.Tasks; public class LimitedSemaphore { private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private int _waitingCount = 0; // 待機中のタスク数 /// <summary> /// セマフォの取得 /// </summary> /// <param name="timeoutMs">待機タイムアウト(ミリ秒)</param> /// <param name="cancellationToken">キャンセルトークン</param> public async Task<bool> TryEnterAsync(int timeoutMs = 0, CancellationToken cancellationToken = default) { // まず即取得を試みる if (await _semaphore.WaitAsync(0, cancellationToken)) { return true; } // 実行中のタスクがある場合、待機タスクが1つまで許可 if (Interlocked.CompareExchange(ref _waitingCount, 1, 0) != 0) { return false; // 既に待機タスクがいる場合はキャンセル } try { // 待機タスクとしてセマフォ取得を試みる if (await _semaphore.WaitAsync(timeoutMs, cancellationToken)) { return true; } return false; } finally { // 待機タスクが完了したらカウントを減らす Interlocked.Exchange(ref _waitingCount, 0); } } /// <summary> /// セマフォの解放 /// </summary> public void Release() { _semaphore.Release(); } }
動作確認のため、Unityで以下のテストスクリプトを作成しました。
・ScopedFlagTest.cs
using System.Threading; using System.Threading.Tasks; using UnityEngine; public class SemaphoreSlimTest2 : MonoBehaviour { private LimitedSemaphore limitedSemaphore = new LimitedSemaphore(); public async void ButtonEvent() { // スレッドをブロックする(待機中のタスクがあればスキップ) if (!await limitedSemaphore.TryEnterAsync(100000)) { Debug.Log($"{name}: 他のタスクが待機中なのでスキップ"); return; } // 1秒ごとに1~3のカウントダウンをログに出力する for (int count = 1; count <= 3; count++) { Debug.Log(count); await Task.Delay(1000); } // スレッドを解放する limitedSemaphore.Release(); } }

処理を実行してみます。
前回記事と違い、複数回ボタンを押下すると2つ目以降の待機は即キャンセルされるようになりました。
