类属性在delphi中是否安全?

时间:2013-11-15 18:39:46

标签: multithreading delphi memory-management

我想使用我自己的类和一些属性。我可以使用readwrite来自定义如何以及(例如)私有变量进行写入或读取。

一个例子是这个类中的整数(MyInteger):

type
  TMyClass = class
  private
    MyInteger : Integer;
    function SomeFunction : Integer;
  public
    property TheInteger : Integer read SomeFunction write MyInteger;
end;

如果(例如)该类在另一个线程从当前实例的另一个线程访问MyInteger(写入)时不断访问(读取)TheInteger会不安全?

希望你们知道我的意思...基本上我不知道如果多个线程同时访问内存中的var是否安全...(没有关键部分)

编辑:

这个班级之间是否也存在差异:

type
  TMyClass = class
  private
    MyInteger : Integer;
    function SomeFunction : Integer;
  public
    property TheInteger : Integer read MyInteger write MyInteger;
end;

和此:

type
  TMyClass = class
  private
    function SomeFunction : Integer;
  public
    MyInteger : Integer;
end;

2 个答案:

答案 0 :(得分:4)

你问:在线程安全方面是否存在任何差异:

type
  TMyClass = class
  private
    MyInteger : Integer;
  public
    property TheInteger : Integer read MyInteger write MyInteger;
end;

type
  TMyClass = class
  public
    MyInteger : Integer;
end;

完全没有区别。编译器为两个变体生成相同的代码。编译器根本不插入同步代码。

实际上,编译器在任何情况下都不会插入线程同步代码。某些库函数具有线程同步,例如TInterfacedObject._AddRef等,但编译器本身从不写同步代码。总是由你的程序员来处理线程安全问题。


有关更具体的示例,请查看问题中的特定代码。

您的共享变量是一个整数。只要整数对齐,即存储在4字节边界上,就永远不会撕裂。读访问永远不会观察到部分写入。

您的方案是单个写入线程和多个读取线程。您没有指定额外的排序约束。有了这些事实和约束,就不需要任何锁定。任何数据竞争必须是良性的,并且无锁代码在语义上与使用锁或互锁访问的代码无法区分。

您何时需要同步访问?

  • 如果您有多个写入线程,则需要同步访问权限。
  • 如果您的变量未对齐或比指针宽,则需要进行同步。

答案 1 :(得分:3)

变量所在的位置并不意味着任何类型的线程安全。这是两个完全不同的概念。

使变量线程安全的原因在于您管理对它的访问权限。

例如,这将是线程安全的,因为操纵FMyInt的唯一方法是通过三种方法中的一种,这些方法都以线程安全的方式实现:

type
  TMyClass = class
  strict private
    FMyInt : Integer;
  public
    procedure IncrementValue;
    function QueryValue : Integer;
    function SubtractValue( aWhat : Integer) : Integer;
  end;

procedure TMyClass.IncrementValue;
begin
  InterlockedIncrement( FMyInt);
end;

function TMyClass.QueryValue : Integer;
begin
  result := InterlockedExchangeAdd( FMyInt, 0);
end;

function TMyClass.SubtractValue( aWhat : Integer) : Integer;
begin
  result := InterlockedExchangeAdd( FMyInt, -aWhat);
end;

这样,但效率会降低。互锁并不适合所有人,所以它真的取决于用例应使用什么方法。

type
  TMyClass = class
  strict private
    FMyInt : Integer;
    FLock : TCriticalSection;
  public
    constructor Create;
    destructor Destroy;  override;
    procedure IncrementValue;
    function QueryValue : Integer;
    function SubtractValue( aWhat : Integer) : Integer;
  end;

constructor TMyClass.Create;
begin
  inherited;
  FLock := TCriticalSection.Create;
end;

destructor TMyClass.Destroy;
begin
  FreeAndNil( FLock);
  inherited;
end;

procedure TMyClass.IncrementValue;
begin
  FLock.Enter;
  try
     Inc(FMyInt);
  finally
    FLock.Leave;
  end;
end;

function TMyClass.QueryValue : Integer;
begin
  FLock.Enter;
  try
    result := FMyInt;
  finally
    FLock.Leave;
  end;
end;

function TMyClass.SubtractValue( aWhat : Integer) : Integer;
begin
  FLock.Enter;
  try
    Dec( FMyInt, aWhat);
    result := FMyInt;
  finally
    FLock.Leave;
  end;
end;

请注意,如果我将值放入record并且有一堆函数和过程进行操作,则同样有效。存储变量的位置无关紧要。

Do&不要的

永远不要混用不同类型的锁。例如,将Interlocked-Functions与TCriticalSection混合,或将TCriticalSection与TMutex混合,或将任何类型的锁与完全无人看守的访问混合。这样做会导致失败,因为不同类型的锁彼此不了解。

请记住,大多数锁机制只是逻辑锁,这并不能真正阻止您对数据做一些疯狂的事情。因此,尽可能地封装对数据的访问以保持控制是一种很好的方法。