gpt4 book ai didi

delphi - DUnit的XPObserver.pas中TXPSubject.DeleteObserver和TXPSubject.DeleteObservers有区别的原因?

转载 作者:行者123 更新时间:2023-12-03 18:27:17 26 4
gpt4 key购买 nike

我很难理解为什么 XPObserver.pas 中的 TXPSubject.DeleteObserver 和 TXPSubject.DeleteObservers 的实现不同的原因?具体来说,“Dispose”和“ReleaseSubject”调用的不同顺序,以及不同顺序对 TXPSubject.DeleteObserver 很重要的原因。有问题的代码被提取并显示在下面。该文件可从 DUnit 获得。

function TXPSubject.DeleteObserver(const Observer: IXPObserver;
const Context: pointer): boolean;
var
idx: integer;
ObserverInfo: TXPObserverInfo;

begin
FSync.Enter;

try
// Check for existence or prior removal
Result := FindObserver(Observer, Context, idx);

if Result then
begin
// Need local ref after deletion from list. Order of Delete() &
// ReleaseSubject() is important here for correct functioning of _Release
// ...***DON'T*** refactor this method!!
ObserverInfo := PXPObserverInfo(FObservers[idx])^;
// Release our (list) reference to observer
PXPObserverInfo(FObservers[idx])^.Observer := nil;
System.Dispose(FObservers[idx]);
FObservers.Delete(idx);
end;

// Exit critical section here as we now have local vars only (thread-safe)
// and call to ReleaseSubject below on last reference will leave FSync
// invalid (destroyed).
finally
FSync.Leave;
end;

// Notify Observer to release reference to us. This will result in
// a call to TXPSubject._Release.
if Result then
ObserverInfo.Observer.ReleaseSubject(IXPSubject(ObserverInfo.Subject),
ObserverInfo.Context);

end;


procedure TXPSubject.DeleteObservers;
var
idx: integer;
ObserverInfo: PXPObserverInfo;

begin
FDeletingObservers := true;
// Count *down* to allow for side-effect of loop actions -
// referenced item will be deleted from list, and remainder will move down
// one slot.
for idx := FObservers.Count - 1 downto 0 do
begin
ObserverInfo := FObservers[idx];
// Notify Observer to release reference to Subject
ObserverInfo^.Observer.ReleaseSubject(IXPSubject(ObserverInfo.Subject),
ObserverInfo^.Context);
// Release our (list) reference to Observer
ObserverInfo^.Observer := nil;
System.Dispose(ObserverInfo);
FObservers.Delete(idx);
end;

FDeletingObservers := false;
end;

最佳答案

第二种方法是更明显的实现,它不关心线程安全。第一个是棘手的。

第一种方法使用临界区 FSync为了线程安全。由于TXPSubjectTInterfacedObject (在提供的代码中看不到),当删除对该对象的最后一个引用时,它将被销毁。这意味着当使用 DeleteObserver 删除最后一个观察者时调用 ReleaseSubject在最后一个观察者上(应该将对主题的引用设置为 nil ),对象将被销毁。现在如果 ReleaseSubject会在锁内被调用,那么该行

FSync.Leave;

导致访问冲突,因为对象(因此也是 FSync )已被销毁。

所以不要调用 ReleaseSubject从锁中,创建观察者信息的本地副本
ObserverInfo := PXPObserverInfo(FObservers[idx])^;

这取消了存储在观察者列表中的指针,基本上创建了一个本地副本。然后,在 FSync 之后锁已被释放, ReleaseSubject在该本地副本上调用。在那之后 Self不再被引用,因此对象已被销毁并不重要。

编辑 : 谁来电 DeleteObserver仍然持有对 ISubject 的有效引用所以它不应该被破坏,直到这个引用超出范围。所以这个答案中描述的场景只有在观察者本身调用 DeleteObserver 时才会发生。 (可能在其析构函数中)在稍后由 ReleaseSubject 重新发布的引用上.

关于delphi - DUnit的XPObserver.pas中TXPSubject.DeleteObserver和TXPSubject.DeleteObservers有区别的原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19938824/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com