什么时候调用TInterfacedObject.Destroy(一个ScopedLock类)

时间:2010-12-08 15:11:59

标签: delphi interface synchronization locking

我想知道何时销毁TInterfacedObject派生类的实例以及谁调用析构函数。我写了一个ScopedLock类,它应该自动调用Release 实例超出范围时的同步对象的方法。它是从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 ?

它在一个简单的测试用例中运行良好,但它是否安全?

3 个答案:

答案 0 :(得分:6)

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

另一方面,如果你编写一个返回接口引用的函数(例如一个名为Create的类函数)(而不是对象引用),则不需要实际声明一个变量来保存接口引用。编译器将创建一个隐藏的本地来接收返回值(因为所有托管类型,如接口和字符串实际上是通过传递结果变量返回的)。这个隐藏的本地行为就像显式本地一样。

我在这里写了更多关于它的信息:http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html

答案 1 :(得分:5)

是的,那就是保存。你的代码

function Example.Foo: Integer;
var
  lock : ILock;
begin
  lock := ScopedLock<TCriticalSection>.Create(mySync);
  // ...
end;

编译为以下伪代码

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;

你可以看到当 lock var 超出范围时,它的引用计数递减,变为零并且锁定对象被自动销毁。

答案 2 :(得分:0)

我没有看到你所支持的方法,虽然正确,但实际上比旧的Try / Finally更好。您已经采用了清晰明确的解决方案,并将其替换为模糊且不透明的解决方案。

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

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