2012-04-16 7 views
7

私は、テストのために、自分のタイマーメソッド(FooMethod)を一度に1つだけ実行したいという状況があります。次の例では、FooMethodがデリゲートとしてタイマーに渡されます。このクラスの多くの具体的な例があります。私は、_lockerを静的にすることで、FooMethod()の1つのインスタンスだけが一度に処理すると考えました。しかし、私がアプリケーションを実行すると、TryEnter()行を複数のスレッドが一度に通過しています。Generic Classを使用したMonitor.TryEnter

これは、新しいタイマーに各クラスを追加する方法です。これは、各FOOインスタンスに対して、ループ内で、行われる:

_timers.Add(new Timer(foo.FooMethod, null, 0, 10000)); 

、これはそのメソッドを有するクラスである:

public class Foo<T> 
{ 
    private static readonly object _locker = new object(); 

    public void FooMethod(object stateInfo) 
    { 
     // Don't let threads back up; just get out 
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 

注:通常は、_lockerは静的ではありません。私は完了するチャンスを得る前に、同じスレッドがメソッドに入ることを望んでいません。私はテストのために静的に変更しました。

私の最初の考えは、クラスが一般的なので動作していない可能性がありますか?そして、それぞれの具象クラスは実際には独自のクラスであり、_locker変数を共有していませんか?本当?それが本当の場合、具体的なクラスを_locker変数をどのように共有する必要がありますか? Foosがアクセスする他のクラスに静的な_locker変数を追加する必要がありますか?

+0

非常に興味深い質問です。 +1 – spender

+2

同じスレッド*がロックを再入力できることに注意してください(ただし、再帰がある場合のみ)。これは、ロックのためのものです:*異なる*スレッドが同じリソースを使用するのを防ぎます。 – phoog

+0

@spenderありがとうございます。そして、私は理解しています。私の静的変数の目標は、複数のスレッドが同時にそのメソッドを処理しないようにすることでした。しかし、それは動作していません。 –

答えて

7

Foosがアクセスできる別のクラスにstatic _locker変数を追加する必要がありますか。 にアクセスする必要がありますか?

はい。

それぞれのFoo<T>タイプは、異なるT引数を持ち、独自の静的_lockerオブジェクトを持ちます。あなたはFooを基底クラスから継承させて静的オブジェクトをそこに置くことができます。そして、すべての型は同じインスタンスを使用します。

6

たぶん

public class Foo 
{ 
    protected static readonly object _locker = new object(); 
} 

public class Foo<T> : Foo 
{ 
    public void FooMethod(object stateInfo) 
    {   
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 
+0

ありがとうございます。これはこれを解決する上品な方法の良い例です。 –

0

非ジェネリック型として、このクラスを作成してください。それはあなたの必要性を目的とするでしょう。

public class Foo 
{ 
    private static readonly object _locker = new object(); 

    public void FooMethod(object stateInfo) 
    { 
     // Don't let threads back up; just get out 
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 
+0

しかし、私はこのクラスを理由のための一般的なものとして持っています。私はそれを元に戻したくありません。 –

2

あなたは正しいです。各固有タイプTはコードで参照され、Foo<T>の新しいコンクリートタイプを生成し、それぞれ独自の静的メンバーセットを持ちます。

コードを次のように再構成できます。それは多くの有効なバリエーションの1つです。

public class Foo 
{ 
    private static readonly object _locker = new object(); 

    public void FooMethod(object stateInfo) 
    { 
     // Don't let threads back up; just get out 
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 

public class Foo<T> 
{ 
    public void FooMethod(object stateInfo) 
    { 
     Foo.FooMethod(stateInfo); 
    } 
} 

また、あなたが複数回実行からのコールバックを防ぐために、無限のperiodでタイマーを開始することができますことを心に留めておきます。 FooMethodの最後にもう一度Changeと呼び出して、タイマーを再度キューに入れます。複数のタイマーがすべて同時に実行されているので、同時に複数の同時実行が行われますが、少なくとも1つのタイマーにつき1つのアクティブなコールしか存在しません。それはまさにあなたが求めているものではありませんが、とにかくこれを指摘すると思いました。

_timers.Add(new Timer(foo.FooMethod, _timers.Count, 10000, Timeout.Infinite)); 

public class Foo<T> 
{ 
    public void FooMethod(object stateInfo) 
    { 
     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      int index = (int)stateInfo; 
      _timers[index].Change(10000, Timeout.Infinite); 
     } 
    } 
} 
+0

良いもの。ありがとう、ブライアン! –

+0

ところで、私は2番目の例がかなり重大な競争状態にあることを指摘しておきます。 'FooMethod'はタイマーが' List'に追加される前に呼び出されます。私が読んでいて、スレッドセーフではないコレクションで同じことを書いているということは、心配しないでください。明らかに私の2番目の例はいくつかの追加作業を必要としますが、あなたはその考えを得ています。 –