MSDNのドキュメントには独特の癖があるような気がします。
最近はときどき拾い読みする程度しかMSDNのドキュメントを読みませんが、昔は根掘り葉掘り集中的に読んだことがあるので、免疫ができているようです。
いかなる資料にも正しい(らしい)読み方というのがあって、それを会得するまでは愚痴が出るのも仕方がない気がします。
特に、長年メンテされているMSDNは、特定コミュニティ向けに書かれているわけですから、門外漢が読みにくいと感じたり、不親切だと感じるのはある程度やむをえない気がします。
UNIXのmanpagesやinfoも不親切さでは負けてない気がします。
# くその役にも立たない余計なトリビアばかりのページも多いし
前置きが長くなりましたが、本題に入ります。
スレッドプールの便利メソッドがあります。
ThreadPool.RegisterWaitForSingleObject メソッド (WaitHandle, WaitOrTimerCallback, Object, Int32,
サンプルを動かしたくらいの知識しかありませんが、スタンドアロンのサンプルは、今なら、以下にあります。
RegisteredWaitHandle.Unregister メソッド (System.Threading)
- using System;
- using System.Threading;
- // TaskInfo contains data that will be passed to the callback
- // method.
- public class TaskInfo
- {
- public RegisteredWaitHandle Handle = null;
- public string OtherInfo = "default";
- }
- public class Example
- {
- public static void Main(string[] args)
- {
- // The main thread uses AutoResetEvent to signal the
- // registered wait handle, which executes the callback
- // method.
- AutoResetEvent ev = new AutoResetEvent(false);
- TaskInfo ti = new TaskInfo();
- ti.OtherInfo = "First task";
- // The TaskInfo for the task includes the registered wait
- // handle returned by RegisterWaitForSingleObject. This
- // allows the wait to be terminated when the object has
- // been signaled once (see WaitProc).
- ti.Handle = ThreadPool.RegisterWaitForSingleObject(
- ev,
- new WaitOrTimerCallback(WaitProc),
- ti,
- 1000,
- false
- );
- // The main thread waits three seconds, to demonstrate the
- // time-outs on the queued thread, and then signals.
- Thread.Sleep(3100);
- Console.WriteLine("Main thread signals.");
- ev.Set();
- // The main thread sleeps, which should give the callback
- // method time to execute. If you comment out this line, the
- // program usually ends before the ThreadPool thread can execute.
- Thread.Sleep(1000);
- // If you start a thread yourself, you can wait for it to end
- // by calling Thread.Join. This option is not available with
- // thread pool threads.
- }
- // The callback method executes when the registered wait times out,
- // or when the WaitHandle (in this case AutoResetEvent) is signaled.
- // WaitProc unregisters the WaitHandle the first time the event is
- // signaled.
- public static void WaitProc(object state, bool timedOut)
- {
- // The state object must be cast to the correct type, because the
- // signature of the WaitOrTimerCallback delegate specifies type
- // Object.
- TaskInfo ti = (TaskInfo)state;
- string cause = "TIMED OUT";
- if (!timedOut)
- {
- cause = "SIGNALED";
- // If the callback method executes because the WaitHandle is
- // signaled, stop future execution of the callback method
- // by unregistering the WaitHandle.
- if (ti.Handle != null)
- ti.Handle.Unregister(null);
- }
- Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
- ti.OtherInfo,
- Thread.CurrentThread.GetHashCode().ToString(),
- cause
- );
- }
- }
直感的に分かりにくいのは、RegisterWaitForSingleObject()の戻り値をどうやってコールバックに知らせるか、だと思いますが、その点についてもさらっとサンプルで示されています。
動かしてみても問題ないですね。ただし、RegisterWaitForSingleObject()発行とほぼ同時にシグナルったりするような無茶な実装だとコールバック時にti.Handleがnullのままの可能性があるかもしれませんね。ですがそれは
コールバックの準備ができていないのに
シグナルっちゃうのが間違い
なだけのような気がします。典型的な「運よく動作してる」パターンの実装じゃないでしょうか。
C#で以下の1行がアトミック動作するはずだ、とか思い込むのは勝手ですけしね。
ti.Handle = ThreadPool.RegisterWaitForSingleObject(
コメント