Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 何时调用TInterfacedObject.Destroy(ScopedLock类)_Delphi_Interface_Synchronization_Locking - Fatal编程技术网

Delphi 何时调用TInterfacedObject.Destroy(ScopedLock类)

Delphi 何时调用TInterfacedObject.Destroy(ScopedLock类),delphi,interface,synchronization,locking,Delphi,Interface,Synchronization,Locking,我想知道TInterfacedObject派生类的实例何时被销毁,以及谁调用析构函数。我编写了一个ScopedLock类,它应该会自动调用发布 实例超出范围时同步对象的方法。它是C++中已知的RAII概念,但我不知道当锁实例超出作用域时是否保证析构函数被调用。 ILock = interface end; ScopedLock<T: TSynchroObject> = class(TInterfacedObject, ILock) strict private sync_

我想知道TInterfacedObject派生类的实例何时被销毁,以及谁调用析构函数。我编写了一个ScopedLock类,它应该会自动调用发布 实例超出范围时同步对象的方法。它是C++中已知的RAII概念,但我不知道当锁实例超出作用域时是否保证析构函数被调用。
ILock = interface
end;

ScopedLock<T: TSynchroObject> = class(TInterfacedObject, ILock)
strict private
    sync_ : T;
public
    constructor Create(synchro : T); reintroduce;
    destructor Destroy;override;
end;

implementation
{ ScopedLock<T> }

constructor ScopedLock<T>.Create(synchro: T);
begin
    inherited Create;;
    sync_ := synchro;
    sync_.Acquire;
end;

destructor ScopedLock<T>.Destroy;
begin
    sync_.Release;
    inherited;
end;

{ Example }
function Example.Foo: Integer;
var
  lock : ILock;
begin
  lock := ScopedLock<TCriticalSection>.Create(mySync);
  // ...
end;  // mySync released ?
ILock=接口
结束;
ScopedLock=class(TInterfacedObject,ILock)
严格保密
同步:T;
公众的
构造函数创建(synchro:T);重新引入;
毁灭者毁灭;推翻
结束;
实施
{ScopedLock}
构造函数ScopedLock.Create(synchro:T);
开始
继承创造;;
同步:=同步;
同步获取;
结束;
析构函数ScopedLock.Destroy;
开始
同步释放;
继承;
结束;
{示例}
函数示例。Foo:Integer;
变量
锁:ILock;
开始
lock:=ScopedLock.Create(mySync);
// ...
完mySync发布了吗?

它在一个简单的测试用例中可以正常工作,但是它安全吗?

是的,这就是save。你的代码

function Example.Foo: Integer;
var
  lock : ILock;
begin
  lock := ScopedLock<TCriticalSection>.Create(mySync);
  // ...
end;
函数示例。Foo:Integer;
变量
锁:ILock;
开始
lock:=ScopedLock.Create(mySync);
// ...
结束;
编译为以下伪代码

function Example.Foo: Integer;
var
  lock : ILock;
begin
  lock := ScopedLock<TCriticalSection>.Create(mySync);
  lock._AddRef;  // ref count = 1
  try
// .. 
  finally
    lock._Release;  // ref count = 0, free lock object
  end;
函数示例。Foo:Integer;
变量
锁:ILock;
开始
lock:=ScopedLock.Create(mySync);
锁定。_AddRef;//参考计数=1
尝试
// .. 
最后
锁定。_释放;//ref count=0,自由锁定对象
结束;

您可以看到,当lock var超出范围时,它的ref计数将减少,变为零,并且lock对象将自动销毁。

唯一的问题是如果函数是内联的:包含ILock引用的局部变量将提升到调用内联函数的函数的范围。这可能会导致锁的寿命比您想要的长

另一方面,如果您编写一个返回接口引用(与对象引用相反)的函数(例如,名为Create的类函数),则无需实际声明一个变量来保存接口引用。编译器将创建一个隐藏的局部变量来接收返回值(因为所有托管类型(如接口和字符串)实际上都是通过传递结果变量返回的)。这个隐藏的局部将像显式局部一样工作


我在这里写了更多关于它的内容:

我不认为你所支持的方法,虽然是正确的,但实际上比旧的Try/Finally好。你已经采取了一个清晰而明确的解决方案,并用一个模糊而不透明的解决方案取代了它

真正的Delphi代码充满了Try/Finally,因此它应该是一个自然的习惯用法。我看不出写作的坏处:

mySync.Acquire;
Try
  //do stuff
Finally
  mySync.Release;
End;

因为它的代码更少?或者,因为它使作用域在创建时显式显示。可能还有其他原因。Try/finally并不坏,但是其他技术也可以很好。你知道:)@David M既然你必须习惯于阅读和解析Try/finally,那么无论它在哪里工作,你都可以使用它。在我看来,有多种方法来保护资源(泄漏或序列化),只是在读取和检查代码时会造成额外的复杂性。我想我来自Python学派的“一种做事的方式”,而不是Perl学派的“多种方式”@Daivd H:对我来说,“做事情的一种方法”就是尽可能使用RAII技术。我不会错的。每当我获得一个序列化时,我确信我会在正确的地方发布,而不编写额外的代码行,我迟早会忘记的。@Hansmad如果你所有的代码库都使用RAII习惯用法,那么这对我来说似乎是一个合理的方法-当你混合使用RAII并尝试/最终我会觉得不舒服。Delphi中RAII的一个缺点是,您不能在C++中比程序级别更本地化。对我来说,另一个不利于RAII的因素是它击中了堆,因为我的应用程序是高度多线程的,性能对我来说就是一切,避免堆是至关重要的。。你能告诉我delphi编译器是如何决定内联函数的吗?有什么方法可以阻止/强制内联吗?@Hansmad:除非你激活了{$INLINE AUTO},否则它永远不会自动内联一个没有内联指令标记的函数。但是$INLINE AUTO并没有使用一个非常智能的启发式方法——它会将每个函数内联到一个特定的大小之下,并且很容易导致大小爆炸。这意味着$INLINE AUTO不太可能生效。因此,如果您自己在过程头的末尾用“inline”指令将函数标记为内联,那么您只需要担心它。