2016-10-21 7 views
13

コールスタック内の以下のいずれのメソッドでもロックを使用しないでコード内の特定のメソッドが呼び出されたことを検出する方法はありますか?
目的は、障害のあるアプリケーションをデバッグして、特定のコードがスレッドセーフでないかどうかを調べることです。メソッドがロックなしで呼び出されたことの検出

+0

ReSharperのは、メンバ変数を外部時々ロックの内側に時々使用される状況を検索します。 –

+0

@Bernhard Hiller、この分野のResharperの能力は非常に限られています。限られたケースのサブセットしか捕捉しない静的解析に基づいています。 – user626528

+0

通常の「ロック(オブジェクト)」文を使用して、何も変更せずにそれを実行できるようにしたいですか?私はあなたがデバッグモードで必要な情報を提供するいくつかのオブジェクトラッパーを使用してそれを行うことができると想像することができます。 – Evk

答えて

1

これはAOP(アスペクト指向プログラミング)のまともな使用例のようです。 AOPの非常に基本的な要約は、コードを乾燥させ、モジュール化するためにクロスカットの問題を扱う方法だということです。アイデアは、各メソッドの開始時と終了時にログを追加するのではなく、オブジェクトのすべてのメソッド呼び出し(例:各呼び出しのログ)を行う場合、オブジェクトを継承してクラス外で行うことですその目的は泥ではない。

これはいくつかの方法で行うことができ、私は2つの例を挙げます。最初は手動です(これは素晴らしいことではありませんが、小さなキャセットの場合は非常に簡単に行うことができます)。

DoerにはDoとOtherという2つのメソッドがあるとします。あなたはそれを継承し、

public class Doer 
{ 
    public virtual void Do() 
    { 
     //do stuff. 
    } 

    public virtual void Other() 
    { 
     //do stuff. 
    } 
} 

public class AspectDoer : Doer 
{ 
    public override void Do() 
    { 
     LogCall("Do"); 
     base.Do(); 
    } 

    public override void Other() 
    { 
     LogCall("Other"); 
     base.Other(); 
    } 

    private void LogCall(string method) 
    { 
     //Record call 
    } 
} 

あなたが唯一のクラスを気が、すぐにあなたが多くのクラスのためにそれをしなければならない場合には実現不可能になった場合にこれは素晴らしいですを作ることができます。そのような場合は、CastleProxyライブラリのようなものを使用することをお勧めします。これは、任意のクラスをラップするためのプロキシを動的に生成するライブラリです。 IOCと組み合わせると、アプリケーション内のすべてのサービスを簡単にラップすることができます。

ここで、CastleProxyを使用しての簡単な例の主なポイントは使用ProxyGenerator.GenerateProxyているとメソッドの周りのものを行うためにIInterceptorsに渡すコール:今すぐ

[Test] 
    public void TestProxy() 
    { 
     var generator = new ProxyGenerator(); 
     var proxy = generator.CreateClassProxy<Doer>(new LogInterceptor()); 
     proxy.Do(); 
     Assert.True(_wasCalled); 
    } 

    private static bool _wasCalled = false; 
    public class LogInterceptor : IInterceptor 
    { 
     public void Intercept(IInvocation invocation) 
     { 
      Log(invocation.Method.Name); 
      invocation.Proceed(); 
     } 

     private void Log(string name) 
     { 
      _wasCalled = true; 
     } 
    } 

、ログ部分を。私はあなたがロックレスであることを本当に必要としているかどうか確信していません。短いロックでは十分かもしれませんが、あなたが思うように進むことができます。

ロックフリーの操作をサポートするC#のツールはよくわかりませんが、この中で最も単純なのはInterlockedを使用して、メソッド内のインスタンスの数をいつでも増やすことです

 [Test] 
    public void TestProxy() 
    { 
     var generator = new ProxyGenerator(); 
     var proxy = generator.CreateClassProxy<Doer>(new LogInterceptor()); 
     proxy.Do(); 
     Assert.AreEqual(1, _totalDoCount); 
    } 

    private static int _currentDoCount = 0; 
    private static int _totalDoCount = 0; 
    public class LogInterceptor : IInterceptor 
    { 
     public void Intercept(IInvocation invocation) 
     { 
      if (invocation.Method.Name == "Do") 
      { 
       var result = Interlocked.Increment(ref _currentDoCount); 
       Interlocked.Increment(ref _totalDoCount); 
       if(result > 1) throw new Exception("thread safe violation"); 
      } 


      invocation.Proceed(); 
      Interlocked.Decrement(ref _currentDoCount); 
     } 
    } 

インターロックは、スレッドセーフな操作を行うために魔法のレジスタの魔法を使用しています(スワップの比較-AND-私は信じて、私は本当に知らない):このような何かを見て。単に「起こった」よりも多くの文脈が必要な場合。並行スタックまたは同時ロックを使用することができます。これらのキューはロックなしです(インターロックも使用します:https://msdn.microsoft.com/en-us/library/dd997305.aspx/)。私は、それらが発生した順序で要素を返すことを約束するかどうかを知るために十分に使用していないので、これらにタイムスタンプを含めることにします。

上記のように、あなたは自由な操作をロックする必要はありませんが、これは必要です。私はあなたの正確な問題を知らないので、これのどれかがあなたにとって完璧なものかどうかはわかりませんが、これに取り組むためのツールを提供する必要があります。

+0

はい、 'Interlocked.Increment'は、アトミックのコンペ・アンド・スワップ操作を使用します。 – Georg

0

host the CLR yourselfとなり、IHostSyncManager::CreateMonitorEventメソッドを使用して取得したロックを追跡できます。あなたは、あなたのホストから、あなたのメソッドに、 "IsLockTaken()"という独自のメカニズムを公開する必要があります。その後、実際のコードであなたのメソッドから呼び出すことができます。

私はそれは可能だと思いますが、それはあなたが解決しようとしている問題からかなりの労力とほぼ確実に気を散らすでしょうが、間違いなく多くの楽しみです!

ここでデッドロックの検出に興味深い読み取りだhttps://blogs.msdn.microsoft.com/sqlclr/2006/07/25/deadlock-detection-in-sql-clr/

関連する問題