2010-12-08 8 views
4

と呼ばれています。私は、インスタンスがスコープの外に出たときに自動的に同期オブジェクトのリリース メソッドを呼び出す必要がありScopedLockクラスを、書きました。これは、C++から知られているRAIIの概念ですが、ロックインスタンスがスコープ外に出たときにデストラクタが呼び出されることが保証されているかどうかはわかりません。はいつTInterfacedObject.DestroyがTInterfacedObjectは、派生クラスのインスタンスが破棄されたとき、私は思ったんだけど、誰がデストラクタを呼び出す(ScopedLockクラス)

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 ? 

単純なテストケースでは問題なく動作しますが、安全ですか?

答えて

5

はい、それは保存しています。あなたのコード

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

あなたはロックVARがスコープの外に出たときにその参照カウントがデクリメントされていることを確認することができ、以下の疑似コード

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; 

としてコンパイルされ、ゼロとなり、ロックオブジェクトが自動的に破棄されます。

6

関数がインライン展開されている場合にのみ問題がある:ILOCKの参照を含むローカル変数は、インライン関数を呼び出す関数のスコープに昇格されます。ロックがあなたが望むよりも長く生きるかもしれません。

一方、(オブジェクト参照ではなく)インタフェース参照を返す関数(たとえばCreateというクラス関数)を記述すると、実際にインタフェース参照を保持する変数を宣言する必要はありません。コンパイラーは、戻り値を受け取るために非表示のローカルを作成します(結果変数を渡すことによって、インターフェースやストリングのような管理されたタイプはすべて実際に戻されます)。この非表示のローカルは、明示的なローカルと同様に動作します。

私はここでそれについての詳細を書いた:http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html

+1

良い記事..デルファイコンパイラが関数をインライン化する方法を教えてください。インライン化を防止/実施する方法はありますか? – hansmaad

+0

@hansmaad:あなたが持っている場合を除き、{$ INLINE AUTO}アクティブ、それが自動的にインライン指令でマークされていない機能をインライン化することはありません。しかし、$ INLINE AUTOは非常にスマートなヒューリスティックを使用していません - それはあるサイズ以下のすべての関数をインライン化し、簡単にサイズの爆発につながります。つまり、$ INLINE AUTOが有効になることはほとんどありません。したがって、プロシージャヘッダーの最後にあるインラインディレクティブを使用してインライン展開の機能をマークしておくと、心配する必要はありません。 –

0

を、私はあなたが支持するアプローチは、正しいながら、古き良きのtry /最後よりも実際に優れていることがわかりません。あなたは明確かつ明示的な解決法を採用し、それを不明瞭で不透明なものに置き換えました。

実際の作業Delphiのコードは、try /最後のいっぱいあるので、それは自然なイディオムでなければなりません。私は文章の欠点を見ることができません:

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

コードが少ないのでまたは、スコープが作成された時点で明示的に明示的になるためです。おそらく他の理由もあります。 try/finallyは悪くはありませんが、他のテクニックも良いことがあります。 –

+0

@David M読んで、解析するのに慣れなければならないことを考えれば、試してみてください/最後にとにかく、とにかくリソースを保護するための複数の方法(リークやシリアライゼーション)があると、コードを読み込んでチェックするときに余分な複雑さが生じます。私は、 "たくさんの方法"のPerl学校とは対照的に、私はPythonの学校から "物事を行う一つの方法"だと思います! –

+4

@Daivd H:私にとっては、「物事を行う方法」は可能な限りRAIIテクニックを使用することです。私はそれで間違って行くことはできません。シリアライズを取得するたびに、適切な場所でリリースされることを確信しています。コードの追加行を書くことなく、遅かれ早かれ忘れてしまいます。 – hansmaad