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!!
最悪だよこれ。