ThreadPool.RegisterWaitForSingleObject は狂っている

http://msdn.microsoft.com/ja-jp/library/system.threading.threadpool.registerwaitforsingleobject.aspx

こういうやつで、要は WaitHandle がシグナルしたらスレッドプールを使ってコールバックしてくれる。

public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,
WaitOrTimerCallback callBack,
Object state,
int millisecondsTimeOutInterval,
bool executeOnlyOnce
)

これだけなら使いやすそうなんだけど、問題は戻り値。


このメソッドによって返される RegisteredWaitHandle を使い終わったら、その RegisteredWaitHandle.Unregister メソッドを呼び出して待機ハンドルへの参照を解放します。 executeOnlyOnce に true を指定した場合でも、常に RegisteredWaitHandle.Unregister メソッドを呼び出すことをお勧めします。 登録された待機ハンドルのファイナライザーに依存するのではなく、RegisteredWaitHandle.Unregister メソッドを呼び出すことで、ガベージ コレクションはより効率的に動作します。

つまり、こうしろといっている。


var handle = ThreadPool.RegisterWaitForSingleObject(…);

handle.Unregister();

え? いや、せっかく register したのに直後に unregister したら意味ないじゃん。じゃあ、コールバックされたハンドラの中で unregi… っても、戻り値だから値を受け渡せねえ!どうすんだこれ!!

ネットだとこんな例がある:


static RegisteredWaitHandle handle;

void f() {
    handle = ThreadPool.RegisterWaitForSingleObject(delegate {
        handle.Unregister();
    }, …);
}

これって、一見、たいていの場合は動くけど、並列プログラミング的にはダメだよ。RegisterWaitFor…から戻ってきて、変数に値が格納される前にコールバックされると handle は null だ。それにそもそもこんなAPIモデルは破綻してる。

 

たちが悪いのは、どうもhandle.Unregisterをしないとデリゲートを参照しっぱなしにすること。

このせいでメモリリークが頻発する。

どうやって使うんだよこのAPI!!

最悪だよこれ。