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来完成此任务 - 以及 Anthony 关于使用 try/finally 的建议。不过很可能会很乱。还要考虑您是否希望重入基于每个线程或每个实例。(多个线程是否可以调用方法开始?)

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

于 2008-11-24T11:32:18.830 回答
1

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

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

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

例如:

if (inFunction1) 
   return;

try
{
  inFunction1 = true;

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

如果这是为了线程安全,您必须小心该变量。

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

确保它被标记为 volatile,如下所示:

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

这个线程有点旧,但我认为值得将它带到 2012 年,因为这个问题仍然存在(或多或少)。我能够使用使用 Reflection.Emit (特别是使用LinFu.DynamicProxy)生成的代理对象来解决这个问题。LinFu 文章比这篇文章更早,所以我认为它讨论的所有内容在被问到时都是相关的(但不知何故今天仍然存在)。

我使用 LinFu 是因为我已经将它用于其他目的,但我确信其他一些可用的 DynamicProxy 框架对您有用(例如 Castle.DynamicProxy),或者您可以基于 Reflection.Emit 推出自己的框架(不适用于那些性格软弱)。它们提供了一种机制,可以填补 AOP 的大部分角色,同时让您控制自己的代码。

于 2012-07-14T03:48:37.847 回答
0

我不认为这是可能的。

最接近的是“同步”属性,但这将阻止所有后续调用。

于 2008-11-24T11:30:55.480 回答
-1

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

于 2016-02-22T12:24:17.597 回答