【问题标题】:Are class properties thread safe in delphi?delphi中的类属性线程安全吗?
【发布时间】:2013-11-15 18:39:46
【问题描述】:

我想使用我自己的具有一些属性的类。我可以使用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;

?

【问题讨论】:

  • 不,它不是线程安全的,您需要保护共享资源免受并发访问,无论是使用临界区还是联锁 API,这是您的选择。
  • AFAIK,Delphi 中没有任何东西在外部是线程安全的。使用线程本身的东西当然应该是,但在外面,你需要始终考虑线程安全。
  • @Benjamin:这正是你想知道的。答案很明确:不,他们不是。您必须使用 CS 进行保护,就像我发布的链接所说的那样。

标签: multithreading delphi memory-management


【解决方案1】:

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

使变量线程安全与否的关键在于您管理对它的访问的方式。

例如,这将是线程安全的,因为操作 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;

这也是如此,但效率较低。 Interlocked 并不适合所有情况,所以它真的取决于用例应该使用什么方法。

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 并有一堆函数和过程进行操作,则同样有效。变量的存储位置无关紧要。

注意事项

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

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

【讨论】:

  • 这个答案没什么意义。无锁代码和带锁或互锁访问的代码之间有什么语义差异。请您告诉我语义上可观察到的差异。如果只有一个线程写入,为什么要使用锁?没有撕裂。任何种族只能是良性的。请给我一个失败模式的示例,原因是单线程修改的整数类型变量缺少锁定。
  • 我们可以保证对齐吗?
  • @WarrenP 是的,我们可以。我们只需要避免打包东西。
  • 所以我们可以在单元顶部强制打包,这是真的。
【解决方案2】:

你问在线程安全方面是否有任何区别:

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 字节边界上,就永远不会发生撕裂。读取访问永远不会观察到部分写入。

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

什么时候需要同步访问?

  • 如果有多个写线程,需要同步访问。
  • 如果您的变量未对齐,或者比指针宽,那么您需要同步。

【讨论】:

  • @JensG 哦。我是否错过了说明有多个写入线程的问题部分。还是整数变量未对齐?即使我这样做了,我的要点也是准确的。
  • 不。你还有两次尝试。
  • @JensG 我放弃了。你得告诉我。
  • 至少在我读到它时,他要求的是更一般的规则,而不仅仅是你谈到的非常具体的案例。我可以阅读诸如“例如”和“一般安全”和“多线程访问”之类的内容(请注意,没有一种访问指定)。
  • @JensG 他说:“如果(例如)类会不断访问(读取)MyInteger,而另一个线程从当前实例的另一个线程访问(写入)TheInteger,这是否安全?”使用另一个线程意味着单个写入线程。如果这不是他的意思,那真的不是我的问题。我必须按要求回答问题。如果他要求单写线程,但实际上是指多写线程,我不能指望他读懂他的想法。我的回答在技术上有什么问题吗?
猜你喜欢
  • 1970-01-01
  • 2021-11-07
  • 2010-10-14
  • 2015-02-15
  • 2010-11-17
  • 2010-09-09
  • 1970-01-01
相关资源
最近更新 更多