7

テストのために、タイマー メソッド (FooMethod) を一度に 1 つだけ実行したい状況があります。以下の例では、FooMethod がデリゲートとしてタイマーに渡されます。このクラスには多くの具象インスタンスがあります。_locker を static にすることで、一度に処理される FooMethod() のインスタンスは 1 つだけになると思いました。しかし、アプリを実行すると、一度に複数のスレッドが 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 がアクセスできる他のクラスに static _locker 変数を追加する必要がありますか?

4

4 に答える 4

7

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

はい。

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

于 2012-04-16T20:28:13.187 に答える
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);
        }
    }
}
于 2012-04-16T20:29:17.373 に答える
2

あなたは正しいです。コードで参照される一意の型ごとTに、CLR が新しい具象型を生成し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また、コールバックが複数回実行されるのを防ぐために、無限でタイマーを開始できることに注意してください。Changeの最後にもう一度呼び出しFooMethodて、タイマーを再度キューに入れます。一度に複数のタイマーがすべて実行されるため、同時に複数の同時実行が実行されますFooMethodが、少なくとも現在は、タイマーごとにアクティブな呼び出しが 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);
        }
    }
}
于 2012-04-16T20:34:01.293 に答える
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);
        }
    }
}
于 2012-04-16T20:33:28.063 に答える