今回は、ManualResetEventSlimクラスについて。
ManualResetEventSlimクラスは、.NET 4.0からSystem.Threading名前空間に追加されたクラスです。
元々、System.Threading.ManualResetEventクラスというのが存在していて
それの軽量版という位置づけになります。短い時間で待機する場合は
こちらを利用した方がパフォーマンスが良くなります。
(短い時間の待機の場合やイベントがプロセスをまたがらない場合、ManualResetEventSlimクラスはビジースピンを行う為。)
同じように追加されたクラスとして、SemaphoreSlimクラスというのもあります。
こちらも、Samaphoreクラスの軽量版として追加されています。
で、このManualResetEventSlimクラスですが、基本的に元となっているManualResetEventクラスと
機能的に変わりません。
ただし、以下のメソッドが追加されています。
Waitメソッドに、CancellationTokenを受け付けるオーバーロードが追加されている。
CancellationTokenは、.NET 4.0から追加されたキャンセル処理のための新たな仕組みです。
タスク並列ライブラリ (TPL) でよく利用されます。
キャンセルトークンを指定するWaitメソッドの場合、通常のシグナル状態になるまで待機すると同時に
キャンセルされたか否かも検知するようになります。
注意点として、キャンセルトークンを指定した場合、キャンセル操作が行われると
OperationCanceledExceptionが発生するので、try-catchが必須となります。
以下、サンプルです。
#region ManualResetEventSlimSamples-01 /// <summary> /// ManualResetEventSlimクラスについてのサンプルです。 /// </summary> /// <remarks> /// ManualResetEventSlimクラスは、.NET 4.0で追加されたクラスです。 /// 元々存在していたManualResetEventクラスよりも軽量なクラスとなっています。 /// 特徴しては、以下の点が挙げられます。 /// ・WaitメソッドにCancellationTokenを受け付けるオーバーロードが存在する。 /// ・非常に短い時間の待機の場合、このクラスは待機ハンドルではなくビジースピンを利用して待機する。 /// </remarks> public class ManualResetEventSlimSamples01 : IExecutable { public void Execute() { // // 通常の使い方. // ManualResetEventSlim mres = new ManualResetEventSlim(false); ThreadPool.QueueUserWorkItem(DoProc, mres); Console.Write("メインスレッド待機中・・・"); mres.Wait(); Console.WriteLine("終了"); // // WaitメソッドにCancellationTokenを受け付けるオーバーロードを使用。 // mres.Reset(); CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = Task.Factory.StartNew(DoProc, mres); // // キャンセル状態に設定. // tokenSource.Cancel(); Console.Write("メインスレッド待機中・・・"); try { // // CancellationTokenを指定して、Wait呼び出し。 // この場合は、以下のどちらかの条件を満たした時点でWaitが解除される。 // ・別の場所にて、Setが呼ばれてシグナル状態となる。 // ・CancellationTokenがキャンセルされる。 // // トークンがキャンセルされた場合、OperationCanceledExceptionが発生するので // CancellationTokenを指定するWaitを呼び出す場合は、try-catchが必須となる。 // // 今回の例の場合は、予めCancellationTokenをキャンセルしているので // タスク処理でシグナル状態に設定されるよりも先に、キャンセル状態に設定される。 // なので、実行結果には、「*** シグナル状態に設定 ***」という文言は出力されない。 // mres.Wait(token); } catch (OperationCanceledException cancelEx) { Console.Write("*** {0} *** ", cancelEx.Message); } Console.WriteLine("終了"); } void DoProc(object stateObj) { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.Write("*** シグナル状態に設定 *** "); (stateObj as ManualResetEventSlim).Set(); } } #endregion
実行結果は以下のようになります。
メインスレッド待機中・・・*** シグナル状態に設定 *** 終了 メインスレッド待機中・・・*** 操作はキャンセルされました。 *** 終了
別スレッドでシグナル状態に設定する前に、キャンセル操作を行っているので
シグナル状態に設定されずにキャンセルされています。
上記のサンプルの
tokenSource.Cancel();
の部分をコメントアウトして実行すると
メインスレッド待機中・・・*** シグナル状態に設定 *** 終了 メインスレッド待機中・・・*** シグナル状態に設定 *** 終了
となります。
追記)
後、ちょこっとした違いですが
ManualResetEventSlimクラスは、EventWaitHandleクラスを継承していません。
元となる待機ハンドルを取得する場合は
- WaitHandle
プロパティから取得します。
================================
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場