今回も、SemaphoreSlimクラスについて。
SemaphoreSlimクラスは、.NET 4.0からSystem.Threading名前空間に追加されたクラスです。
従来から存在していたSemaphoreクラスの計量版という位置づけになります。
SemaphoreSlimクラスのWaitメソッドには、キャンセルトークン(CancellationToken)を
受け付けるオーバーロードが存在します。
利用方法は、以前記述したCountdownEventやBarrierなどと同じで
キャンセルが発行されるとOperationCanceledExceptionが発生します。
[参考記事]
- .NET クラスライブラリ探訪-045 (System.Threading.CountdownEvent(3))(キャンセルトークンの利用, CancellationToken, Wait, .NET 4.0)
- .NET クラスライブラリ探訪-048 (System.Threading.Barrier(2))(バリア, キャンセル処理, CancellationToken, .NET 4.0)Comments
以下、サンプルです。
#region SemaphoreSlimSamples-02 /// <summary> /// SemaphoreSlimクラスについてのサンプルです。 /// </summary> /// <remarks> /// SemaphoreSlimクラスは、.NET 4.0から追加されたクラスです。 /// 従来から存在していたSemaphoreクラスの軽量版となります。 /// </remarks> public class SemaphoreSlimSamples02 : IExecutable { public void Execute() { // // SemaphoreSlimのWaitメソッドにはキャンセルトークンを // 受け付けるオーバーロードが存在する。 // // CountdownEventやBarrierの場合と同じく、Waitメソッドに // キャンセルトークンを指定した場合、別の場所にてキャンセルが // 行われると、OperationCanceledExceptionが発生する。 // const int timeout = 2000; CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; using (SemaphoreSlim semaphore = new SemaphoreSlim(2)) { // // あらかじめ、セマフォの上限までWaitしておき // 後のスレッドが入れないようにしておく. // semaphore.Wait(); semaphore.Wait(); // // 3つのタスクを作成する. // 1つ目のタスク:キャンセルトークンを指定して無制限待機. // 2つ目のタスク:キャンセルトークンとタイムアウト値を指定して待機. // 3つ目のタスク:特定時間待機した後、キャンセル処理を行う. // Parallel.Invoke ( () => WaitProc1(semaphore, token), () => WaitProc2(semaphore, timeout, token), () => DoCancel(timeout, tokenSource) ); semaphore.Release(); semaphore.Release(); Console.WriteLine("CurrentCount={0}", semaphore.CurrentCount); } } // キャンセルトークンを指定して無制限待機. void WaitProc1(SemaphoreSlim semaphore, CancellationToken token) { try { Console.WriteLine("WaitProc1=待機開始"); semaphore.Wait(token); } catch (OperationCanceledException cancelEx) { Console.WriteLine("WaitProc1={0}", cancelEx.Message); } finally { Console.WriteLine("WaitProc1_CurrentCount={0}", semaphore.CurrentCount); } } // キャンセルトークンとタイムアウト値を指定して待機. void WaitProc2(SemaphoreSlim semaphore, int timeout, CancellationToken token) { try { bool isSuccess = semaphore.Wait(timeout, token); if (!isSuccess) { Console.WriteLine("WaitProc2={0}\t★★タイムアウト★★", isSuccess); } } catch (OperationCanceledException cancelEx) { Console.WriteLine("WaitProc2={0}", cancelEx.Message); } finally { Console.WriteLine("WaitProc2_CurrentCount={0}", semaphore.CurrentCount); } } // 特定時間待機した後、キャンセル処理を行う. void DoCancel(int timeout, CancellationTokenSource tokenSource) { Console.WriteLine("待機開始:{0}msec", timeout + 1000); Thread.Sleep(timeout + 1000); Console.WriteLine("待機終了"); Console.WriteLine("★★キャンセル発行★★"); tokenSource.Cancel(); } } #endregion
実行結果は以下のようになります。
WaitProc1=待機開始 待機開始:3000msec WaitProc2=False ★★タイムアウト★★ WaitProc2_CurrentCount=0 待機終了 ★★キャンセル発行★★ WaitProc1=操作はキャンセルされました。 WaitProc1_CurrentCount=0 CurrentCount=2
一つ目のタスクは、キャンセルトークンのみを指定しているので無制限に待機しています。
二つ目のタスクは、キャンセルトークンとタイムアウトを両方指定しています。
なので、先にタイムアウトを迎えて処理が終了しています。
三つ目のタスクが、所定時間を経過後にキャンセルを発行すると、一つ目のWaitはキャンセルされ
OperationCanceledExceptionが発生しています。
================================
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場