Delphi:使用Critical Sections的首选保护方式

时间:2011-11-12 01:07:24

标签: multithreading delphi

我有一个对象x,需要从几个(5个以上的线程)访问。对象的结构是

    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
   constructor Create; <--- Create the criticalsection here
   destructor 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
   constructor Create; <--- Create the criticalsection here
   destructor Destroy; <--destroy it here
 etc;

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

端;

并始终正常访问该对象。我猜第一个选项更优雅,即使两种方法都能正常工作。你评论?

3 个答案:

答案 0 :(得分:7)

转到选项B,使关键部分在对象内部。如果该类的用户必须使用外部函数来安全地访问该实例,则不可避免地会有人不会和房子一起倒塌。

您还需要考虑您希望保护哪些操作语义免受多个并发读取的影响。写道。如果在getter和setter中放置一个锁,则可以保证对象内部一致,但对象的用户可能会看到多线程工件。例如,如果线程A将10写入对象的属性,而线程B将50写入同一对象的该属性,则只有其中一个可以是最后一个。如果A恰好先行,则A将观察到他们给房子写了一张10,但是当他们再次阅读时,他们看到B的50在那里读到了A的写后读写间隙。

另请注意,您并不需要锁来保护单个整数字段。对齐指针大小的整数写入是当今几乎所有硬件系统上的原子操作。你肯定需要一个锁来保护多块数据,比如结构或多步操作,比如同时更改两个相关字段。

如果有任何方法可以重新设计你的设计,使这些对象在线程上的特定操作中是本地的,那就去做吧。制作本地数据副本可能会略微增加内存占用,但它可以大大简化多线程代码,并且比在整个应用程序中留下互斥锁地雷运行得更快。寻找其他简化假设 - 如果您可以设置系统以使对象在多个线程可用时是不可变的,那么该对象根本不需要任何锁定保护。只读数据适合跨线程共享。非常非常好。

答案 1 :(得分:6)

使CS成为对象的成员并使用属性getter / setter方法中的CS是正确的方法。另一种方法不起作用,因为它在实际访问对象之前锁定和解锁CS,因此属性值根本不受保护。

答案 2 :(得分:5)

一种简单的方法是在对象周围使用一个线程安全的包装器,类似于TThreadList。包装器需要两个方法:Lock(进入临界区并返回内部对象)和Unlock(离开临界区)。