8

現時点では、次のような関数がいくつかあります。

private bool inFunction1 = false;
public void function1()
{
    if (inFunction1) return;
    inFunction1 = true;

    // do stuff which might cause function1 to get called
    ...

    inFunction1 = false;
}

次のように宣言できるようにしたいと思います。

[NoReEntry]
public void function1()
{
    // do stuff which might cause function1 to get called
    ...
}

再入を防ぐために関数に追加できる属性はありますか? そうでない場合、どのように作成しますか?関数呼び出しの前後にコードを追加するために使用できる AOP 属性について聞いたことがあります。彼らは適切でしょうか?

4

9 に答える 9

18

bool を使用して直接設定する代わりに、long と Interlocked クラスを使用してみてください。

long m_InFunction=0;

if(Interlocked.CompareExchange(ref m_InFunction,1,0)==0)
{
  // We're not in the function
  try
  {
  }
  finally
  {
    m_InFunction=0;
  }
}
else
{
  // We're already in the function
}

これにより、チェックスレッドが安全になります。

于 2008-11-24T11:47:45.430 に答える
5

アセンブリと IL の書き換えがなければ、説明した方法でコードを変更するカスタム属性を作成する方法はありません。

たとえば、単一の引数の関数の場合は、代わりにデリゲートベースのアプローチを使用することをお勧めします。

static Func<TArg,T> WrapAgainstReentry<TArg,T>(Func<TArg,T> code, Func<TArg,T> onReentry)
{
    bool entered = false;
    return x =>
    {
        if (entered)
            return onReentry(x);
        entered = true;
        try
        {
            return code(x);
        }
        finally
        {
            entered = false;
        }
    };
}

このメソッドは、ラップする関数 ( Func<TArg,T> と一致すると仮定します。他のバリアント、またはより多くの労力を必要とする完全に汎用的なバージョンを作成できます) と、再エントリの場合に呼び出す代替関数を受け取ります。(代替関数は、例外をスローしたり、すぐに戻ったりする可能性があります。) 次に、渡されたメソッドを通常呼び出すコード全体で、代わりに WrapAgainstReentry() によって返されるデリゲートを呼び出します。

于 2008-11-24T11:37:41.097 に答える
4

PostSharp属性を作成して、メソッドの名前が現在のスタックトレースにあるかどうかを確認できます。

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static bool IsReEntry() {
        StackTrace stack = new StackTrace();
        StackFrame[] frames = stack.GetFrames();

        if (frames.Length < 2)
            return false;

        string currentMethod = frames[1].GetMethod().Name;

        for (int i = 2; i < frames.Length; i++) {
            if (frames[i].GetMethod().Name == currentMethod) {
                return true;
            }
        }

        return false;
    }
于 2008-11-24T14:57:10.757 に答える
2

これを実現するためにPostSharpを使用できることがわかるかもしれません- try/finally の使用に関する Anthony からの提案とともに。混む可能性大ですが。また、再入可能性をスレッド単位にするかインスタンス単位にするかを検討してください。(複数のスレッドがメソッドを呼び出して開始することはできますか?)

フレーム自体にはこのようなものはありません。

于 2008-11-24T11:32:18.830 に答える
1

このスレッドは少し古いですが、この問題はまだ存在しているので(多かれ少なかれ)、2012年に持ち込む価値があると思いました。Reflection.Emitを使用して(具体的にはLinFu.DynamicProxyを使用して)生成されたプロキシオブジェクトを使用して、この問題を解決することができました。LinFuの記事はこの記事よりも古いので、質問されたときに、それが説明していることはすべて関連性があると思います(それでも、今日でもどういうわけか)。

LinFuを使用したのは、すでに他の目的で使用していたためですが、利用可能な他のDynamicProxyフレームワークのいくつか(Castle.DynamicProxyなど)が機能するか、Reflection.Emitに基づいて独自のフレームワークをロールすることができると確信しています(これらのフレームワークではありません)。性向が弱い)。これらは、コードの制御を維持しながら、AOPの役割の大部分を満たすメカニズムを提供します。

于 2012-07-14T03:48:37.847 に答える
1

これがスレッドセーフのためである場合は、その変数に注意する必要があります。

最初のスレッドが変数を設定する前に、別のスレッドが関数に入り、チェックに合格する可能性があります。

次のように volatile とマークされていることを確認してください。

private volatile bool inFunction1 = false;
于 2008-11-24T11:34:34.817 に答える
1

そのような事前定義された属性はありません。新しい属性を作成できますが、それは役に立ちません。問題は、カスタム属性によってメソッドが再度呼び出されないようにすることです。これは実行可能ではないと思います。

lock ステートメントは、呼び出しがブロックされて待機し、すぐに返されないため、必要なものではありません。

PS: 上記のサンプルで try ... finally ブロックを使用します。それ以外の場合、関数の途中で例外がスローされた場合、inFunction1 は true のままになり、すべての呼び出しはすぐに返されます。

例:

if (inFunction1) 
   return;

try
{
  inFunction1 = true;

  // do stuff which might cause function1 to get called
  ...
}
finally 
{
  inFunction1 = false;
}
于 2008-11-24T11:30:23.873 に答える
0

それが可能になるとは思いません。

最も近いのは 'Synchronized' 属性ですが、それ以降の呼び出しはすべてブロックされます。

于 2008-11-24T11:30:55.480 に答える