2013-07-19 8 views
9

私はメモリリークを追跡しようとしてきた私は、元の歴史の中で、この変更に出くわしたジェダイVCLのJvHidControllerClass.pas、:Delphi:スレッドを「中断しない」ように作成する必要がありますか?

古いリビジョン:

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(True); 
... 
FDataThread.Resume; 

:経由で正しいコードになり

CreateSuspended(False); 

に関するいくつかの秘密がありますか?

誤っ

TMyThread.Create(False) 

を呼び出すことによって焼かれた後、私は決して正しいと私の脳でそれを提出してきました。スレッドをすぐに開始させるための有効な使用はありますか(値を初期化する必要がある場合)?

+0

うわー!!! JVCL on D5!私はそれを終了し、D5の互換性を維持することを止めた後、プラグが切られました。そんなノスタルジックな感じ... –

+1

@ Arioch'Theあまりにも懐かしくはありません。 2009年のJVCL 3.xです。厳密に言えば、Richard MarquandのオリジナルのHidControllerクラスです(2005年)。私はそれを少し助けました。 JVCLが採用したバージョンは、巨大な "jcl-ifying" *を受けました。実際の違いはありません。技術的には私はリチャードのバージョンを使用しています。だから、私はFastMMがキャッチする* use-after-free *クラッシュを修正することができます。 –

答えて

12

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

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

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

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

むしろ私は知っています。

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

+0

それはそれを説明します。 +1履歴レッスン! –

+0

私は常にそれを行う方法は、スレッドの実行で直接インスタンス化/破棄しています。とにかくCOMのようなもの(ADOのようなもの)で作業する必要があるなら、それはとにかく行われなければなりません。だから、実際には、スレッドを書くたびに、私は創造/破壊においてそのようなことについて何も創造も破壊も何も実装しません。 (+1) –

+0

@ジェリー作成者とスレッドの間で通信する必要があるまでは問題ありません。 –

関連する問題