2011-01-14 4 views
2

작업 개체 모음으로 구성된 작업 개체가 있습니다. 각 작업에는 데이터베이스가 자주 체크되어 실행을 취소해야하는지 확인하는 고유 한 WatcherClass이 있습니다. 워크 플로우의 반복에 따라 취소 될 수 있습니다. 취소 된 경우 foreach 블록에서 실행중인 모든 스레드는 취소를 정상적으로 종료하고 종료합니다.동일한 잠금 장치로 잠금 장치 내부의 개체 수정

교착 상태가 발생할 수있는 워처 코드에 문제가 있습니까? Timer.Change (Timeout.Infinite, Timeout.Infinite)를 사용하여 타이머 콜백을 처리하는 스레드를 하나만 허용하려고합니다.하지만 실제로 변경됩니다. WatcherClass.Job 잠금 문이 잠금 문장을 감싸기 때문에 래핑 된 이후로 동일한 잠금 개체에서 _Job에 대해 동일한 가져 오기/설정)? 코드가 정상적으로 작동하는 것으로 보입니다.하지만 실제로는 아무 것도 알 수 없습니다.

메인 스레드에서 코드는 다음에 비슷한 같습니다

using (WatcherClass watcher = new WatcherClass()) 
{ 
    watcher.CancelTokenSource = new CancellationTokenSource(); 
    watcher.Start(); 
    foreach (SomeJob job in worksflow.Jobs) 
    { 
     watcher.Job = job; 
     //Do some stuff async 
     //Do some more stuff async 
    } 

} 

public class WatcherClass : IDisposable 
{ 
    private System.Threading.Timer _WatcherTimer; 
    private readonly object locker = new object(); 
    private bool _Disposed = false; 
    private SomeJob _Job; 

    public SomeJob Job 
    { 
     get 
     { 
      lock (locker) 
      { 
       return _Job; 
      } 
     } 
     set 
     { 
      lock (locker) 
      { 
       _Job= value; 
      } 
     } 
    } 

    public System.Threading.Task.CancellationTokenSource 
     CancelToken { get; set; } 

    public WatcherClass() 
    { 
     _WatcherTimer = new Timer(new TimerCallback(DoCheck), null, 
      Timeout.Infinite, Timeout.Infinite); 
    } 

    public void Start() 
    { 
     _WatcherTimer.Change(30000, Timeout.Infinite); 
    } 

    public void DoCheck(object state) 
    { 

     lock (locker) 
     { 

      if (_Disposed || this.CancelToken.IsCancellationRequested) 
       return; 

      _WatcherTimer.Change(Timeout.Infinite, Timeout.Infinite); 

      //Check database to see if task is cancelled 
      if (cancelled) 
      { 
       this.CancelToken.Cancel(); 
       _Job.CancelResult = CancelResult.CanceledByUser; 
       _Job.SomeOtherProperty = true; 
      } 
      else 
      { 
       //Safe to continue 
       _WatcherTimer.Change(30000, Timeout.Infinite); 
      } 
     } 

    } 

    public void Dispose(bool disposing) 
    { 
     lock (locker) 
     { 
      if (disposing) 
      { 
       if (_WatcherTimer != null) 
        _WatcherTimer.Dispose(); 

      } 
      _Disposed = true; 
     } 
    } 
} 
+0

분명히하기 위해, 이것들은'class task','class CancelToken'이며 TPL의 것들이 아닙니다. –

+0

@Henk : CancelToken은 TPL에서 가져온 것입니다. 작업은 아닙니다.이 예제에서는 작업을 호출했습니다. 감사합니다 –

+0

어쩌면 혼동을 피하기 위해 Job으로 바꿀 수도 있습니다. 특히 CancelToken을 사용할 경우 –

답변

1

당신이 작업 속성 주위와 DoCheck 기능에 aquire 잠금 만 WatcherClass의 내부 _Task 필드에 대한 액세스를 보호합니다. DoCheck에서는 _task 객체 자체의 속성도 수정합니다. 잠금은 다른 스레드로부터 동시에 타스크 오브젝트 필드를 수정하지 못하도록합니다.

응용 프로그램에서 작업 개체가 DoCheck에 의해서만 조작되는 경우에는 대개 괜찮습니다. 작업 객체가 DoCheck 이외의 코드로 조작 될 수있는 경우 문제가있을 수 있습니다.

추가로 생성하는 모든 잠금은 교착 상태의 추가 기회입니다. 다중 잠금은 항상 특정 순서로 획득되는 경우 교착 상태가 발생할 수 있습니다. 코드 플로우가 일부 상황에서 잠금 B 이전에 잠금 A를 획득하도록 허용하거나 다른 상황에서 잠금 A 이전에 B를 잠그면 심각한 교착 상태 위험이 있습니다. 스레드 1은 A를 잠그고 B는 스레드 2가 B를 잠그려고하면 B를 시도하고 A => 교착 상태를 잠그려고합니다.

WatcherClass의 경우 각각 자체 잠금을 가진 여러 개의 watcherclass 인스턴스가있는 경우 조심하십시오 다른 watcherclass 인스턴스에서 잠금을 획득하려고 시도 할 수있는 외부 호출 (또는 화재 이벤트)을 작성하지 마십시오. AB/BA 교착 상태가 발생하기를 기다리고 있습니다.

+0

_task는 foreach 루프의 일부 활동에 의해 수정됩니다. –