サンプルの置き場所のパターン

  • 投稿日:
  • by
  • カテゴリ:

MSDNのドキュメントには独特の癖があるような気がします。

最近はときどき拾い読みする程度しかMSDNのドキュメントを読みませんが、昔は根掘り葉掘り集中的に読んだことがあるので、免疫ができているようです。

いかなる資料にも正しい(らしい)読み方というのがあって、それを会得するまでは愚痴が出るのも仕方がない気がします。

特に、長年メンテされているMSDNは、特定コミュニティ向けに書かれているわけですから、門外漢が読みにくいと感じたり、不親切だと感じるのはある程度やむをえない気がします。

UNIXのmanpagesやinfoも不親切さでは負けてない気がします。

# くその役にも立たない余計なトリビアばかりのページも多いし

 

前置きが長くなりましたが、本題に入ります。

 

スレッドプールの便利メソッドがあります。

ThreadPool.RegisterWaitForSingleObject メソッド (WaitHandle, WaitOrTimerCallback, Object, Int32,

サンプルを動かしたくらいの知識しかありませんが、スタンドアロンのサンプルは、今なら、以下にあります。

 

RegisteredWaitHandle.Unregister メソッド (System.Threading)

Code Snippet
  1. using System;
  2. using System.Threading;
  3.  
  4. // TaskInfo contains data that will be passed to the callback
  5. // method.
  6. public class TaskInfo
  7. {
  8.     public RegisteredWaitHandle Handle = null;
  9.     public string OtherInfo = "default";
  10. }
  11.  
  12. public class Example
  13. {
  14.     public static void Main(string[] args)
  15.     {
  16.         // The main thread uses AutoResetEvent to signal the
  17.         // registered wait handle, which executes the callback
  18.         // method.
  19.         AutoResetEvent ev = new AutoResetEvent(false);
  20.  
  21.         TaskInfo ti = new TaskInfo();
  22.         ti.OtherInfo = "First task";
  23.         // The TaskInfo for the task includes the registered wait
  24.         // handle returned by RegisterWaitForSingleObject.  This
  25.         // allows the wait to be terminated when the object has
  26.         // been signaled once (see WaitProc).
  27.         ti.Handle = ThreadPool.RegisterWaitForSingleObject(
  28.             ev,
  29.             new WaitOrTimerCallback(WaitProc),
  30.             ti,
  31.             1000,
  32.             false
  33.         );
  34.  
  35.         // The main thread waits three seconds, to demonstrate the
  36.         // time-outs on the queued thread, and then signals.
  37.         Thread.Sleep(3100);
  38.         Console.WriteLine("Main thread signals.");
  39.         ev.Set();
  40.  
  41.         // The main thread sleeps, which should give the callback
  42.         // method time to execute.  If you comment out this line, the
  43.         // program usually ends before the ThreadPool thread can execute.
  44.         Thread.Sleep(1000);
  45.         // If you start a thread yourself, you can wait for it to end
  46.         // by calling Thread.Join.  This option is not available with
  47.         // thread pool threads.
  48.     }
  49.  
  50.     // The callback method executes when the registered wait times out,
  51.     // or when the WaitHandle (in this case AutoResetEvent) is signaled.
  52.     // WaitProc unregisters the WaitHandle the first time the event is
  53.     // signaled.
  54.     public static void WaitProc(object state, bool timedOut)
  55.     {
  56.         // The state object must be cast to the correct type, because the
  57.         // signature of the WaitOrTimerCallback delegate specifies type
  58.         // Object.
  59.         TaskInfo ti = (TaskInfo)state;
  60.  
  61.         string cause = "TIMED OUT";
  62.         if (!timedOut)
  63.         {
  64.             cause = "SIGNALED";
  65.             // If the callback method executes because the WaitHandle is
  66.             // signaled, stop future execution of the callback method
  67.             // by unregistering the WaitHandle.
  68.             if (ti.Handle != null)
  69.                 ti.Handle.Unregister(null);
  70.         }
  71.  
  72.         Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
  73.             ti.OtherInfo,
  74.             Thread.CurrentThread.GetHashCode().ToString(),
  75.             cause
  76.         );
  77.     }
  78. }

 

直感的に分かりにくいのは、RegisterWaitForSingleObject()の戻り値をどうやってコールバックに知らせるか、だと思いますが、その点についてもさらっとサンプルで示されています。

動かしてみても問題ないですね。ただし、RegisterWaitForSingleObject()発行とほぼ同時にシグナルったりするような無茶な実装だとコールバック時にti.Handleがnullのままの可能性があるかもしれませんね。ですがそれは

コールバックの準備ができていないのに

シグナルっちゃうのが間違い

なだけのような気がします。典型的な「運よく動作してる」パターンの実装じゃないでしょうか。

C#で以下の1行がアトミック動作するはずだ、とか思い込むのは勝手ですけしね。

ti.Handle = ThreadPool.RegisterWaitForSingleObject(