讨论下lazy-loading和线程安全
大家一定听说过lazy-loading(延迟加载),简单来说,延迟加载就是只有当需要某资源的时候才去加载,以减少不必要的系统开销。如下面的代码所示,仅在访问Members属性的时候创建列表(而不是在constructor内创建)
type
TPerson = class
end;
TMembers = TObjectList<TPerson>;
TGroup = class
private
fMembers: TMembers;
function GetMembers: TMembers;
public
destructor Destroy; override;
//...
property Members: TMembers read GetMembers;
end;
//...
destructor TGroup.Destroy;
begin
fMembers.Free;
inherited Destroy;
end;
function TGroup.GetMembers: TMembers;
begin
if fMembers = nil then
begin
fMembers := TMembers.Create;
end;
Result := fMembers;
end;
// fCriticalSection: SyncObjs.TCriticalSection;
constructor TGroup.Create;
begin
inherited Create;
fCriticalSection := TCriticalSection.Create;
end;
destructor TGroup.Destroy;
begin
fMembers.Free;
fCriticalSection.Free;
inherited Destroy;
end;
function TGroup.GetMembers: TMembers;
begin
fCriticalSection.Enter;
try
if fMembers = nil then
begin
fMembers := TMembers.Create;
end;
Result := fMembers;
finally
fCriticalSection.Leave;
end;
end;
function TGroup.GetMembers: TMembers;
begin
if fMembers = nil then // 先判断fMembers是否为nil,若为nil才进入临界区
begin
fCriticalSection.Enter;
try
if fMembers = nil then // 再次判断fMembers(这就是double-checked locking的由来)
begin
fMembers := TMembers.Create;
end;
finally
fCriticalSection.Leave;
end;
end;
Result := fMembers;
end;
function TGroup.GetMembers: TMembers;
begin
if fMembers = nil then
begin
fCriticalSection.Enter;
try
MemoryBarrier;
if fMembers = nil then
begin
fMembers := TMembers.Create;
end;
finally
fCriticalSection.Leave;
end;
end;
Result := fMembers;
end;
function TGroup.GetMembers: TMembers;
var
list: TMembers;
begin
if fMembers = nil then
begin
list := TMembers.Create;
if InterlockedCompareExchangePointer(fMembers, list, nil) <> nil then
begin
list.Free;
end;
end;
Result := fMembers;
end;
// fMembersSync: SysUtils.IReadWriteSync;
constructor TGroup.Create;
begin
inherited Create;
fMembersSync := TMREWSync.Create; // or TMultiReadExclusiveWriteSynchronizer.Create;
end;
destructor TGroup.Destroy;
begin
fMembers.Free;
inherited Destroy;
end;
function TGroup.FindMember(const name: string): TPerson;
var
person: TPerson;
begin
fMembersSync.BeginRead; // 查找成员是“读操作”
try
for person in Members do
begin
if SameText(person.Name, name) then
begin
Result := person;
Break;
end;
end;
finally
fMembersSync.EndRead;
end;
end;
procedure TGroup.AddMember(person: TPerson);
begin
fMembersSync.BeginWrite; // 添加成员属于“写操作”
try
Members.Add(person);
finally
fMembersSync.EndWrite;
end;
end;
// 这里用读写锁应该没有意义,是不是要用上面的某种方法来保证线程安全???
function TGroup.GetMembers: TMembers;
begin
end;
临界区 成本最高的 ,这句话不认同, 临界区比通过消息,事件等的效率都高,
[解决办法]
倾向使用第4种
[解决办法]
支持下,用过第二种
[解决办法]