TJvThreadがスレッドを実行する仕組みを調べる

JVCLのTJvThreadはTThreadと違い、コンポーネントをフォームに配置して使用します。

TJvThreadのExecute()メソッドを呼ぶと、OnExecuteイベントに記述した処理が別スレッドで実行します。

サンプルプログラムは0から1000までの数を100ミリ秒ごとに表示するプログラムです。
(「Delphi Acid Floor」を参考にしました。)

Unit1.h

class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
  TJvThread *JvThread1;
  TLabel *Label1;
  void __fastcall JvThread1Execute(TObject *Sender, Pointer Params);
  void __fastcall FormShow(TObject *Sender);
private:    // ユーザー宣言
  int FCount;
  void __fastcall CountUp();
public:     // ユーザー宣言
  __fastcall TForm1(TComponent* Owner);
};

Unit1.cpp

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
}
void __fastcall TForm1::FormShow(TObject *Sender)
{
  //スレッドを実行する
  JvThread1->Execute(NULL);
}
/**
 * 別スレッドで実行する処理
 */
void __fastcall TForm1::JvThread1Execute(TObject *Sender, Pointer Params)
{
  for (this->FCount = 0; this->FCount < 1000; this->FCount++)
  {
    JvThread1->Synchronize(this->CountUp);
    Sleep(100);
  }
}
void __fastcall TForm1::CountUp()
{
  Label1->Caption = IntToStr(this->FCount);
}

興味深いのは、サンプルコードではTForm1::JvThread1Execute()は別のスレッドで処理が行われることです。
にもかかわらず、TForm1::JvThread1Execute()内のthisはForm1を指しています。
別スレッドで行われる処理がForm1のprivateなメンバ変数やメンバ関数にアクセスできるため、ソースコードを簡潔に記述できています。

TJvThreadはどのようにして実現しているのか、ソースコードを見てみましょう。

TJvThreadのExecute()が呼ばれると、TThreadのサブクラスであるTJvBaseThreadが作成されます。

function TJvThread.Execute(P: Pointer): TJvBaseThread;
var
  BaseThread: TJvBaseThread;
begin
  BaseThread := TJvBaseThread.Create(Self, FOnExecute, P);

TJvBaseThreadのコンストラクタで、TJvThreadのFOnExecuteメソッドとその引数が渡されています。

constructor TJvBaseThread.Create(Sender: TObject; Event: TJvNotifyParamsEvent; Params: Pointer);
begin
  FExecuteEvent := Event;
  FParams := Params;

スレッドを実行すると、FExecuteEvent(TJvThreadのFOnExecuteメソッド)が実行されます。

procedure TJvBaseThread.Execute;
begin
  FExecuteEvent(Self, FParams);

TJvNotifyParamsEventは次のように定義されています。

TJvNotifyParamsEvent = procedure(Sender: TObject; Params: Pointer) of object;

つまりTJvNotifyParamsEventはメソッドポインタです。
メソッドポインタは、クラスインスタンスへの隠されたポインタを保持しています。
だから、JvThread1ExecuteのthisがForm1を指していました。

メソッドポインタを上手に活用すると、クラスの設計やソースコードが簡潔になりそうだと思いました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください