2016-10-28 5 views
0

複数のスレッド間でアクセスできるIDisposableオブジェクトがあります。私は、オブジェクトがクリーンアップを実行する前に "使用中"であることを追跡する方法を理解しようとしています。つまり、実行中のメソッドが完了すると減少するように、何らかの参照カウントを保持して、Disposeメソッドがすべて完了するまで(またはタイムアウトが経過するまで)続行しないようにする必要があります。 。しかし、Methodへの今後の呼び出しが失敗すると、一度Disposeに入ったことを確認する必要があります。 。のようなIDisposableの同時オブジェクトアクセスのトラッキング

何か:Method()が再び呼び出されると_stoppingが設定されていないが、Disposeは、他のすべてを無効にする、継続するため

class MyObject : IDisposable 
{ 
    private long _counter; 
    private bool _stopping; 
    private IDisposable _someResource; 
    public void Method() 
    { 
     if (_stopping) 
      throw new InvalidOperationException(); 

     Interlocked.Increment(ref _counter); 
     try 
     { 
      // do some work    
     } 
     finally 
     { 
      Interlocked.Decrement(ref _counter); 
     } 
    } 

    public void Dispose() 
    { 
     var timeout = DateTime.Now.Add(TimeSpan.FromSeconds(15)); 
     while (DateTime.Now < timeout && Volatile.Read(ref _counter) > 0) 
     { 
      // wait 
      // Thread.Sleep(10) or something 
     } 

     _stopping = true; 
     //perform clean up 
     _someResource.Dispose();   
    } 
} 

は、しかし、これは動作しません。

ここで使用できる特定のパターン、またはこれを解決するために使用できるフレームワーククラスがありますか?基本的に双方向信号であるDisposeに何も処理中でないことが伝えられているので、その信号が失敗するはずです。Method

私はCountdownEventを見つけましたが、ここでどのように使用できるかはわかりません。 This answerは、CountdownLatchの例を示していますが、新しい作業が要求されることはありません。

+1

あなたの*クライアント*はあなたの*クライアント*ではなく、*責任を負わせますか?安全な同時アクセスが必要な場合は、他の世界と同様にロックを解除してください。スレッドセーフな 'Dispose'は便利ですが、これは簡単な' Interlocked.CompareExchange'で実現できます。 *他の*操作に関して処理をスレッドセーフにすることは珍しいことです。通常のアプローチは、 'Dispose'が呼び出された瞬間から*将来の操作でオブジェクトを利用できないものとして通知し、まだ進行中の作業を気にかけないようにすることです。 –

+0

"[...]' _stopping'は設定されていません[...] "待っているループの前に値を割り当てるのはなぜですか? – Sidewinder94

+0

私は上記を書きましたが、それが同じトラップに落ちると思った後、最初に設定することについて考えました。なぜですか?私はこれをこのように受け継いだので、私はそれを修正するために何ができるかを見極めています。 – pinkfloydx33

答えて

0

いいえ、Disposeメソッドがクラスを消費するすべてのインスタンスを「追跡」することに責任を負うだけです。クラスの消費者は、クリーンアップを処理し、適切に処分する必要があります。

Disposeを複数のクラスで複数回呼び出すことができる場合は、コードの匂いが見つかりました。 disposeメソッドを呼び出すクラスは1つだけです。そのクラスはDisposeメソッドを使用してクラスのコンシューマを追跡する必要があります。

ディスポーザルが複数回呼び出されないようにするための一般的なパターンは、今持っているのと同様のフラグを設定することです。 を必要に応じて使用してください:

private bool _zombified; 

public void Method() 
{ 
    if (_zombified) 
     throw new ObjectDisposedException(); 

    // method's logic 
} 


public void Dispose 
{ 
    if(_zombified) 
    { 
     return; 
    } 

    _zombified = true; 

    // perform clean-up 
} 
+0

1つのクラスはdisposeを呼び出す責任がありますが、残念ながらそのメソッドを呼び出す(ただし処分しない)他のクラスに渡されます。私はこれを継承し、私はそれが醜いことを十分に認識しています。 – pinkfloydx33

+0

処理コールを削除するために他のコード/クラスが変更されないようにする何かがありますか? –

+0

あなたの質問に私が従っているかわからない – pinkfloydx33

関連する問題