我想使用我自己的类和一些属性。我可以使用read
和write
来自定义如何以及(例如)私有变量进行写入或读取。
一个例子是这个类中的整数(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;
答案 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混合,或将任何类型的锁与完全无人看守的访问混合。这样做会导致失败,因为不同类型的锁彼此不了解。
请记住,大多数锁机制只是逻辑锁,这并不能真正阻止您对数据做一些疯狂的事情。因此,尽可能地封装对数据的访问以保持控制是一种很好的方法。