我可以向函数添加属性以防止重新进入吗?

时间:2008-11-24 11:22:52

标签: c# function attributes reentrancy

目前,我有一些看起来像这样的功能:

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属性;他们会适合吗?

9 个答案:

答案 0 :(得分:16)

不要使用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
}

这将使检查线程安全。

答案 1 :(得分: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&lt; TArg,T&gt; - 您可以编写其他变体,或者更加省力的完全通用版本)以及在重新进入的情况下调用的备用函数。 (备用函数可能抛出异常,或立即返回等)然后,在通常调用传递方法的代码中,调用WrapAgainstReentry()返回的委托。

答案 2 :(得分: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;
    }

答案 3 :(得分:2)

您可能会发现可以使用PostSharp来完成此操作 - 以及Anthony关于使用try / finally的建议。但它可能会很混乱。还要考虑您是否希望重新进入每个线程或每个实例。 (多个线程可以调用方法来启动,还是不?)

框架本身没有这样的东西。

答案 4 :(得分:1)

没有预定义的此类属性。您可以创建新属性,但这对您没有帮助。问题是使自定义属性阻止再次调用该方法,我认为这是不可行的。

lock语句不是你想要的,因为这会导致调用阻塞和等待,而不是立即返回。

PS:在上面的示例中使用try ... finally块。否则,如果在函数中间抛出异常,则inFunction1将保持为true并且所有调用将立即返回。

e.g。 :

if (inFunction1) 
   return;

try
{
  inFunction1 = true;

  // do stuff which might cause function1 to get called
  ...
}
finally 
{
  inFunction1 = false;
}

答案 5 :(得分:1)

如果这是针对线程安全的,则必须小心该变量。

在第一个线程设置变量之前,可能有另一个线程进入函数并传递检查。

确保它标记为易失性:

private volatile bool inFunction1 = false;

答案 6 :(得分:1)

这个帖子有点旧,但我认为值得将它带入2012年,因为这个问题仍然存在(或多或少)。我能够使用Reflection.Emit(特别是使用LinFu.DynamicProxy)生成的代理对象来解决这个问题。 LinFu的文章比这篇文章更早,所以我认为它所讨论的一切都是有问题的(但现在仍然不知道)。

我使用LinFu是因为我已经将它用于其他目的,但我确信其他一些可用的DynamicProxy框架对你有用(例如Castle.DynamicProxy),或者你可以根据Reflection.Emit推出自己的框架(不适合那些性格偏弱的人)。它们提供了一种机制,可以充分发挥AOP的作用,同时让您控制代码。

答案 7 :(得分:0)

我不认为这是可能的。

最接近的是'Synchronized'属性,但这将阻止所有后续调用。

答案 8 :(得分:-1)

您可能需要考虑通过修改设计来避免重入,以便在上一次调用完成之前永远不会调用function1()。对我来说,似乎在function1()上面缺少一层。