2016-10-21 5 views
13

호출 스택의 아래 메서드에서 잠금을 사용하지 않고 코드의 특정 메서드가 호출되었음을 감지하는 방법이 있습니까?
목표는 오류가있는 응용 프로그램을 디버그하고 특정 코드가 스레드로부터 안전하지 않은지 확인하는 것입니다.메서드가 잠금없이 호출되었음을 감지했습니다.

+0

ReSharper에서이 멤버 변수는 때때로 외부 잠금 내부에서 가끔 사용되는 상황을 발견 교착 상태 감지에 흥미로운 읽기입니다. –

+0

@Bernhard Hiller, Resharper의이 분야에서의 능력은 매우 제한적입니다. 제한된 부분 집합 만 잡는 정적 분석을 기반으로합니다. – user626528

+0

일반 "잠금 (객체)"문을 사용하여 수정하지 않고도이를 수행 할 수 있기를 원하십니까? 디버그 모드에서 필요한 정보를 제공 할 일부 객체 래퍼를 사용하여 그렇게 할 수 있다고 상상할 수 있습니다. – Evk

답변

1

이것은 AOP (aspect oriented programming)의 적절한 사용 사례처럼 보입니다. AOP의 아주 기본적인 요약은 코드를 건조하고 모듈화하기 위해 교차 절단 문제를 다루는 방법이라는 것입니다. 아이디어는 각 메소드의 시작과 끝에서 로그를 추가하는 대신 객체에 대한 모든 메소드 호출 (예 : 각 호출 기록) 대신 뭔가를 수행하는 경우 대신 객체를 상속하고 클래스 외부에서 수행합니다 진흙 투성이가 아닙니다.

이것은 몇 가지 방법으로 수행 할 수 있으며 두 가지 예를 들어 설명하겠습니다. 첫 번째는 수동입니다 (이 방법은 훌륭하지 않지만 작은 캐서의 경우 매우 쉽게 수행 할 수 있습니다).

클래스가 있다고 가정하고 Do 및 기타 두 가지 방법이 있습니다. 당신은 그 상속 및

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와 함께 응용 프로그램의 모든 서비스를 쉽게 랩핑 할 수 있습니다.

여기 방법은 호출 주위에 물건을 할 IInterceptors에 주요 포인트 사용 ProxyGenerator.GenerateProxy 인 및 통과, CastleProxy를 사용하는 간단한 예입니다 : 이제

[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#의 많은 도구에 대해 알지 못합니다.하지만이 중 가장 간단한 버전은 인터록을 사용하여 주어진 시간에 메서드에있는 인스턴스의 개수를 증가시키는 것입니다.

 [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); 
     } 
    } 

연동 스레드에게 안전 운전을 할 마법의 레지스터 마법을 사용합니다 (저는 믿습니다 - 그리고 스왑을 비교,하지만 난 정말하지 알고) :이 같은 것을 본다. "It Happened"보다 더 많은 상황이 필요하다면. 잠금이없는 동시 스택이나 동시 대기열을 사용할 수 있습니다 (인터록도 사용합니다 : 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/