2011-01-14 13 views
2

私は、作業項目の集合で構成されるジョブオブジェクトを持っています。各ジョブには、それ自体がWatcherClassに関連付けられており、データベースを頻繁にチェックして実行をキャンセルする必要があるかどうかを確認します。ワークフローのどの繰り返しでもキャンセルすることができます。キャンセルされた場合、foreachブロックから実行されているスレッドはキャンセルを正常に終了して終了します。同じロックでロック内のオブジェクトを変更する

ウォッチャーコードにデッドロックが発生することがありますか? Timer.Change(Timeout.Infinite、Timeout.Infinite)を使用して、1つのスレッドのみをタイマーコールバックで処理できるようにしようとしていますが、私は変更しています。WatcherClass.Jobロックステートメント内では、同じロックオブジェクト内の_Jobのget/setは同じですか?)コードは正常に動作しているように見えますが、実際に何も表示されていないことがわかります。

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

[タスクのプロパティの周りAQUIREとDoCheck機能にのみWatcherClassの内部_Taskフィールドへのアクセスを保護ロック:メインスレッドで

コードは、これに似見えます。 DoCheckでは、_taskオブジェクト自体のプロパティも変更しています。ロックによって他の誰も他のスレッドからタスクオブジェクトのフィールドを同時に変更することはできません。

アプリケーションでタスクオブジェクトがDoCheckで操作されている場合は、おそらく問題ありません。タスクオブジェクトがDoCheck以外のコードで操作される可能性がある場合は、問題が発生する可能性があります。

さらに、作成するすべてのロックがデッドロックの機会になることに注意してください。複数のロックが常に特定の順序で取得される場合、複数のロックはデッドロックフリーになることがあります。ある状況でロックBがロックBの前にロックAを取得できるようにしたり、他の状況でロックAの前にBをロックしたりすると、デッドロックの危険性は非常に高くなります。スレッド1はAをロックし、スレッド2はBをロックし、Aはデッドロックをロックしようとします。

ウォッチャークラスのケースでは、それぞれ独自のロックを持つ複数のウォッチャークラスインスタンスを使用する場合は注意してください他のウォッチャークラスインスタンスでロックを取得しようとする可能性のある外部呼び出し(またはファイアイベント)を作成しないでください。それは起こるのを待っているAB/BAデッドロックです。

+0

_taskは、foreachループ内のいくつかのアクティビティによって変更されます。 –

関連する問題