クラスメソッド版TThread.Synchronizeの使いどころ。
無名メソッドの導入によってSynchronizeメソッドが拡張されたのは知ってる人も多いだろうけど、ついでにクラスメソッド版のSynchronizeも作られたことは俺以外に2人くらいは知らないかもしれない。
宣言部を引用してみるとこんな感じ。
type TThread = class ... protected ... procedure Synchronize(AMethod: TThreadMethod); overload; procedure Synchronize(AThreadProc: TThreadProcedure); overload; public ... class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod); overload; static; class procedure Synchronize(AThread: TThread; AThreadProc: TThreadProcedure); overload; static; end;
で、このクラスメソッド版の方が何をしているかというと、単純に言えば
AThread.Synchronize(AMethod{ or AThreadProc});
としてるだけ。なので一瞬「え?いらなくね?」と思いそうになるんだけど、例えばこんな状況を考えてみると必要かもしれない。
type // 実装部は省略するけど、要はCreateで受け取った無名メソッドをExecute内で実行するクラス THogeThread = class(TThread) private FProc: TProc; protected procedure Execute; override; constructor Create(cosnt Proc: TProc); end; // 全然関係ないプロシージャ procedure Hogehoge; begin THogeThread.Create( procedure begin // ここでSynchronizeする処理を書きたいけど、インスタンスメソッドのSynchronizeはprotectedだし、 // キャストして呼ぶにしてもTProcをTProc<TThread>と変えて、わざわざインスタンスを引数でもらうのもアレだしなぁ… // という時にクラスメソッドをコール TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('コピペ厨は滅べばいいのに!'); end); end); end;
ここで上記コードの説明の前に、本来のSynchronizeがどうやってメインスレッドで処理させてるかという話を先にすると、TSynchronizeRecordというレコードがあって、Synchronizeはここにメソッドポインタ、もしくは無名メソッドポインタなどを入れて専用リストにAddする。一方メインスレッドは「今わりと暇だし処理してやんよ」となったタイミングで、リストから取り出したTSynchronizeRecord内の処理を実行してるんだけど、この渡されるTSynchronizeRecordってどこで確保されてるかというと、TThreadのプライベート変数なんだよね。
その知識を頭に入れた上で上記コードを見ると「あれ?クラスメソッドのSynchronizeにnil渡しちゃったらやばくね?」という疑問がわかざるを得ない。だってプライベート変数のTSynchronizeRecordに実行すべき処理への参照を入れて渡してるんだから、インスタンスがないとTSynchronizeRecordも存在しなくなるわけだし。
そこでクラスメソッド版Synchronizeの処理をもう少しだけ詳しく見てみると、引数AThreadにちゃんとインスタンスが渡された場合は上述の通りインスタンスメソッドのSynchronizeを呼んでるだけなんだけど、nilを渡したときはそれ用の処理が書かれていて、動的にTSynchronizeRecordを生成してメインスレッドに渡すようになってる。だからnilを渡してもOKだし、上記のような無名メソッドを利用した書き方をする際は、わざわざインスタンスを引数としてもらう必要もなくなるので、記述量もちょっとだけ減らせる*1。
まぁ結局何が言いたいかというと、こうやってグダグダ書いてあるのを見てもいまいちピンと来ないので、ソース見た方が早いと思いまーす、ということ。
*1:もちろん完全な等価ではなくて、動的に確保する分わずかに処理が増えると言えば増えるけどそんなの気にする人は神経質すぎて禿げろ