2016-08-06 8 views
3

TStringListからデータを読み取るのは、同期の形式がなくても問題ありませんか?メインスレッドとの同期など。TStringListスレッドは安全ですか?

例コード

var MyStringList:TStringList; //declared globally 

procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer); 
var x:integer; 
begin 

    for x:=0 to MaxInt do MyStringList.Add(FloatToStr(Random)); 

end; 


procedure TForm1.ButtonClick(Sender: TObject); 
var x:integer; 
    SumOfRandomNumbers:double; 
begin 

    for x:=0 to MyStringList.Count-1 do 
    SumOfRandomNumbers:=SumOfRandomNumbers+StrToFloat(MyStringList.Strings[x]); 

end; 

または私はEnterCiticalSection

var MyStringList:TStringList; //declared globally 

procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer); 
var x:integer; 
begin 

    for x:=0 to MaxInt do 
    begin 
    EnterCriticalSection(MySemaphore); 
    MyStringList.Add(FloatToStr(Random)); 
    LeaveCriticalSection(MySemaphore); 
    end; 

end; 


procedure TForm1.ButtonClick(Sender: TObject); 
var x:integer; 
    SumOfRandomNumbers:double; 
begin 

    for x:=0 to MyStringList.Count-1 do 
    begin 

    EnterCriticalSection(MySemaphore); 
    SumOfRandomNumbers:=SumOfRandomNumbers+StrToFloat(MyStringList.Strings[x]); 
    LeaveCriticalSection(MySemaphore); 

    end; 

end; 
+1

同時に書き込みと読み取りができるメモリは、常に保護する必要があります。あなたのケースでは、メインのスレッドを読み取ることができますし、セカンダリスレッドが書き込むことができますので、同期する必要があります。 – whosrdaddy

+2

どうしたのでしょうか?コンパイラはあなたの心を読む必要があります。同期を追加します。 –

答えて

1

でMyStringListへのアクセスを保護する必要はありません、そうではありません。 TStringListの内部には、たとえば.Add()または.GetStrings()などのロック機構がありません。

残念ながらTThreadListのようなものはありません。つまり、TListのスレッドセーフラッパーです。しかし、あなたはそれを自分で簡単に作ることができます。

TThreadStringList = class 
private 
    FStringList: TStringList; 
    FCriticalSection: TRtlCriticalSection; 
    // ... 
public 
    function Add(const S: string): Integer; 
    // ... 
end; 

// ... 

TThreadStringList.Add(const S: string): Integer; 
begin 
    EnterCriticalSection(FCriticalSection); 
    try 
    Result:= Add(S); 
    finally 
    LeaveCriticalSection(FCriticalSection); 
    end; 
end; 

それはあなたが必要とする他のすべてのメソッドにこれを適用するには、簡単に次のようになります。ここでは

はその中で、私はAdd()のためのケースをカバーし、TStringListのの同期デコレータための簡単な例です。あなたはにそれを使用することができ、その後それを削除する前に、あなたはに持っていることを念頭に置いて

ベアは、クリティカルセクションを初期化します。

+0

質問のクリティカルセクションベースのコードは、ロックを間違った場所に配置します。 –

+0

@DavidHeffernanはい、それを明確にするための例を追加しました。 – SevenEleven

+0

まだ動作しません。各メソッドをロックしてすべてのコードをスレッドセーフにすることはできません。 –

15

まず、TStringListはスレッドセーフではありません。
第2に、大部分の場合、複数のスレッドで共有されない低レベルのコンテナにとっては、そうすることを試みるのはひどい考えです。
第3に、あなたがスレッドセーフにするために提案した素朴なコードは悲惨なほど不十分です。本当にスレッドセーフなものにするのは不十分です。これは、一般的にそうしようとする際の問題の一部です。あなたが尋ねる質問のテキストで

は、同期のいずれかの形式なしのTStringListからデータを読み取ることはOKですか?

はいいいです。実際、より効率的であるため、これが好まれます。

しかしデータは、スレッド間でを共有されている場合、あなたが問題に実行します。そのため、スレッド間で共有されるデータ量(文字列リストだけでなく)を最小限に抑える必要があります。データを共有するには、が必要な場合は、適切に制御された方法で行います。ポイント3

あなたのコードはスレッドセーフではありません理由に拡大


は、それが共有アクセスからすべてあなたのデータを保護するには及ばないということです。「私はロックで特定の操作をラップする必要があり、すべては罰金になりますは。」あなたのリストが共有されている場合

ポイントは、あなたはされています:これは、マルチスレッド開発における一般的な誤解である

  • コンテナを表す構造体を共有する。
  • およびあなたはデータメンバー(実際の文字列)を共有しています。
  • 文字列を扱う場合、これはさらに進んでいます。これは、Delphiが文字列を管理する方法が、アプリケーションのまったく異なる領域で同じ値の他の文字列と(内部参照カウントによって)共有できることを意味します。

それはあなたの提案のロック戦略があなたの現在要件に適しているかもしれないことは可能ですが、それははるかに一般的に、スレッドセーフであることからです。

  • は、データ・アクセス・パスを理解する:あなたは、スレッドセーフなコードを書きたい場合は、に上

    結論

    責任はあります。

  • スレッド間の共有を最小限に抑えます。
  • また、データを安全に共有するための最良の戦略を実装する(多くのオプションがあり、いずれの場合でもロックが保証されているわけではありません)。

追記

私は私はあなたが本当に本当のあなたに関する表示を与えているとは考えていないので、あなたのロック技術が唯一の「は、あなたの現在の要件に適したかもしれない」という前に示しましたの要件。あなたは、あなたがが本当に次のことに注意してくださいする必要がありますしている場合:

のコードでは、あなたのTStringList「スレッドセーフ」を作るのは全くメリットがないだろう提示しています。ループ内でリストを作成し、2番目のループで値を読み取ります。あなたはデータを同時に使用することは全くありません。同時にです。

最も近いコードはマルチスレッドになるはずです:UIをブロックしないようにメインスレッドから両方のループを処理することをお勧めします。その場合、バックグラウンドスレッドはで、はそのTStringListインスタンスを共有する必要があります。メインスレッドと簡単に同期して、結果を報告することができます。ないことで

は、あなたが完全にロックの必要性を回避することができ、共有することがを必要としませデータを共有します。それらは不必要なオーバーヘッドになります。また、TStringListにはに「スレッドセーフティ」メカニズムが組み込まれていないと満足できます。

関連する問題