Delphi:クリティカルセクションを使用した優先保護方法

私はいくつかの(5 +スレッド)からアクセスする必要があるオブジェクトxがあります。オブジェクトの構造は次のとおりです。

    Tx = class
    private
      Fx: integer;
    public
      property x: integer read Fx read Fx;
   etc;

より良い(最もエレガントな)保護方法は何ですか?

a)

Tx = class
 private
   Fx: integer;
 public
   property x: integer read Fx read Fx;
 public
   constructまたは Create; <--- Create the criticalsection here
   destructまたは Destroy; <--destroy it here
 etc;

var
   cs: TCriticalSection;
   Obj: Tx;

function GetSafeObject(): Tx;
begin
CS.Enter;
try
Result:= Obj;
finally
CS.Leave;

終わり;

常にオブジェクトにGetSafeObj()としてアクセスします。x:= 3;

または

Tx = class
 private
   Fx: integer;
   FCS: TCriticalSection;
 public
   property x: integer read GerX read SetX;
 public
   constructまたは Create; <--- Create the criticalsection here
   destructまたは Destroy; <--destroy it here
 etc;

where 
 function Tx.Getx(): integer;
 begin
   CS.Enter;
   try
     Result:= Fx;
   finally
     CS.Leave;
   終わり;
終わり;

終わり;

and always access the object nまたはmally. I guess the first option is mまたはe elegant, even if both methods should wまたはk fine. Ay comments?

8
追加された
ビュー: 1
あなたの質問は悪いです。整数prop xにアクセスする方法を記述する必要があります
追加された 著者 David Heffernan,
TOndrejの答えで提案されているように、これらの2つのオプションは使用しませんが、 Lock/Unlock メソッド(またはクリティカルセクションをパブリックにする)に依存します。オプションAは何も保護しません。オプションBは整数で動作しますが、クラスインスタンスを返すと失敗します:プロパティ値のコピーを作成した場合(これは integerstring )これは問題ありませんが、返されたすべてのデータが参照によって返された場合は、そのデータを保護しなければなりません。
追加された 著者 Arnaud Bouchez,

3 答え

オプションBに移動し、クリティカルセクションをオブジェクトの内部に作成します。クラスのユーザーがインスタンスに安全にアクセスするために外部関数を使用する必要がある場合、誰かがそうしないことは避けられず、家が転落することは避けられません。

You also need to think about what operational semantic you are wanting to protect from multiple concurrent reads & writes. If you put a lock inside your getter and setter, you can guarantee that your object is internally coherent, but users of your object may see multithreading artifacts. For example, if thread A writes 10 to a property of your object, and thread B writes 50 to that property of the same object, only one of them can be last one in. If A happens to go first, then A will observe that they wrote a 10 to the property, but when they read it back again they see B's 50 that snuck in there in the gap between A's read-after-write.

また、単一の整数フィールドを保護するためにロックを必要としないことにも注意してください。アライメントされたポインタサイズの整数書込みは、今日のほぼすべてのハードウェアシステム上のアトミック操作です。あなたは間違いなく構造体や複数のステップの操作のようなマルチピースデータを保護するために、同時に2つの関連フィールドを変更するようなロックが必要です。

これらのオブジェクトをスレッド上の特定の操作に対してローカルにするためにデザインを修正する方法があれば、それを行ってください。データのローカルコピーを作成するとメモリ占有量はわずかに増えるかもしれませんが、マルチスレッドのコードを大幅に簡素化し、アプリケーション全体にミューテックス地雷を残すよりも速く実行できます。他の簡単な前提も見てください。複数のスレッドが利用できる間はオブジェクトが不変であるようにシステムを設定できれば、オブジェクトはまったくロック保護を必要としません。読み取り専用データはスレッド間で共有するのに適しています。とてもとても良い。

7
追加された
アトミック・ライトは、読者が部分的に書かれた価値を読み取ることがないように、スレッド・セーフを意味します。多値セマンティクス(同じ操作で更新する必要のある複数のデータ)があり、すべての値が常に互いにコヒーレントである必要がある場合、アトミック書き込みは無意味です。
追加された 著者 dthorpe,
保護が必要なリソースが整数フィールドの場合、内部ロックが保証されているか、まったく必要ない可能性があります。保護が必要なリソース(一貫性のある状態を維持する必要がある)がオブジェクト全体である場合、TThreadListなどのインスタンスへの排他的アクセスを取得するチェックアウトモデルが適切です。 PS、私はTThreadListを書きました。 ;>
追加された 著者 dthorpe,
@ArnaudBouchez OPは整数フィールドを持つスレッドの安全性について尋ねました。代わりに、実際に整数フィールドの代わりにクラスのような複合/多値型を使用している場合は、間違った質問をしました。
追加された 著者 dthorpe,
アトミック操作はスレッドセーフではありません。実装に依存します。詳しくは、こちらをご覧ください。
追加された 著者 LU RD,
ロック/アンロックの方法が理にかなっている理由と、TOndrejの答えが私にとってうまく聞こえる理由です。
追加された 著者 Arnaud Bouchez,

CSをオブジェクトのメンバーにし、プロパティゲッター/セッターメソッドの内部でCSを使用することが正しいアプローチです。もう1つのアプローチは、オブジェクトが実際にアクセスされる前にCSをロックしてロックを解除するので機能しません。したがって、プロパティ値はまったく保護されません。

6
追加された

TThreadList()のように、スレッドセーフなラッパーをオブジェクトの周りに配置するのが簡単な方法です。 ラッパーには、 Lock (クリティカルセクションを入力して内部オブジェクトを返す)と Unlock (クリティカルセクションを離れる)の2つのメソッドが必要です。

5
追加された
+1このような方法でユーザーコードをもう少し冗長にしても(特に Lock; try ... finally Unlock; end; )、これはパフォーマンスに最適なオプションです(特に、安全にマルチスレッドの前に、またはオブジェクトのいくつかのプロパティにアクセスしたい場合)、複雑な型の競合状態の識別(整数の使用はOKですが、プロパティがクラスインスタンスを返す場合はプロセス全体明示的にオブジェクトを使用しないようにするまで、より良い保護を受けるでしょう)。
追加された 著者 Arnaud Bouchez,