9

Jedi VCL のメモリ リークを追跡しようとしていましたがJvHidControllerClass.pas、ソース履歴で次の変更に遭遇しました。

古いリビジョン:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  inherited Create(True);
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
end;

現在のリビジョン:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  inherited Create(False);
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
end;

経験から、中断されていないスレッドを作成すると、次のことがわかりました。

inherited Create(False);

その後、スレッドはすぐに実行を開始します。この場合、まだ初期化されていないオブジェクトにアクセスしようとします。

procedure TJvHidDeviceReadThread.Execute;
begin
   while not Terminated do
   begin
     FillChar(Report[0], Device.Caps.InputReportByteLength, #0);
     if Device.ReadFileEx(Report[0], Device.Caps.InputReportByteLength, @DummyReadCompletion) then

すぐに塗りつぶしReportて、オブジェクトにアクセスしようとしますDevice。問題は、それらがまだ初期化されていないことです。これらは、スレッドが開始された後の次の行です。

  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);

これは競合状態であることを認識しています。また、ユーザーが本番環境でクラッシュを経験する可能性はかなり低いため、レース クラッシュを放置してもおそらく問題はありません。

しかし、私は道を外れていますか?何か不足していますか?呼び出します:

BeginThread(nil, 0, @ThreadProc, Pointer(Self), Flags, FThreadID);

スレッドを開始してすぐに実行しませんか? これは本当に (意図的に) JVCL に追加された競合状態の回帰ですか? 何か秘密はありますか

CreateSuspended(False);

それは正しいコードになります:

CreateSuspended(True);
...
FDataThread.Resume;

?

間違えて電話して火傷した後

TMyThread.Create(False)

私は決して正しくないので、私の脳にそれをファイルしました。(値を初期化する必要がある場合) スレッドをすぐに開始できる有効な用途はありますか?

4

1 に答える 1

12

これは、Delphi 5 での の実装に関する基本的な設計上の欠陥ですTThread。基礎となる Windows スレッドは、のコンストラクターで開始されTThreadます。それはあなたが説明するレースにつながります。

RTL の Delphi 6 バージョンでは、スレッド開始メカニズムが変更されました。Delphi 6 以降、スレッドは で開始されTThread.AfterConstructionます。そして、コンストラクターが完了した後に実行されます。これにより、コードの競合がなくなります。

Delphi 6 以降では、基礎となる Windows スレッドはコンストラクタで作成されますが、フラグTThreadを使用して中断されて作成されます。CREATE_SUSPENDEDその後、 で、 であるAfterConstruction限り、スレッドが再開されます。TThread.FCreateSuspendedFalse

Delphi 5 でこの問題を回避する 1 つの方法は、継承されたコンストラクタを最後に呼び出すことです。このような:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
  inherited Create(False);
end;

かなり醜い私は知っています。

したがって、コンストラクターが完了したら中断して再開するスレッドを作成するというアプローチの方がおそらく優れています。このアプローチは、Delphi 6 以降で RTL が問題を解決する方法を反映しています。

于 2013-07-19T14:21:53.987 に答える