2008-11-24 41 views
6

目前,我有一些功能,这是这样的:我可以向功能添加属性以防止再入?

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

+0

在多个线程和多个对象实例存在的情况下,此限制的范围是什么?是否可以在任何时候只通过单个线程为单个对象实例执行function1,还是更轻松? – Constantin 2008-11-24 16:05:15

回答

1

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

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

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

例如:

if (inFunction1) 
    return; 

try 
{ 
    inFunction1 = true; 

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

我不认为这是可能的。

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

2

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

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

1

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

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

确保它标志着挥发性像这样:

private volatile bool inFunction1 = false; 
+3

“挥发性”不会解决这种竞争条件。 – Constantin 2008-11-24 15:25:40

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; 
     } 
    }; 
} 

这种方法需要的功能包(假定它匹配函数功能< TARG,T> - 你可以写其它变体,或完全通用版本与更多的努力)和备用函数在再入情况下调用。 (备用函数可能会抛出异常,或立即返回等)。然后,在您通常会调用传递方法的代码中,调用WrapAgainstReentry()返回的委托。

+0

这仍然表现出多线程环境中的固有问题。看到我的帖子关于上面的易变变量。 – 2008-11-24 11:42:47

+0

当然,罗布。这只是一个演示,因为它也只处理一个非常特定的功能签名。 – 2008-11-24 17:56:31

14

而不是使用一个布尔值,并直接将其设置的,请尝试使用一个漫长而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

出于好奇,在这里使用`long`而不是`int`有什么特别的理由吗? (有一个`CompareExchange(ref int,int,int)`超载) – FunctorSalad 2011-03-11 00:54:54

+2

没有特别的原因。我用来做很多Win32编程,而且那里的互锁功能需要很长时间。在上面的例子中使用int不会有任何区别。 – Sean 2011-03-11 07:38:27

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; 
    } 
1

这个线程是有点老,但我想这将会是值得把它纳入2012年,因为这个问题仍然存在(或多或少)。我能够使用Reflection.Emit生成的代理对象(具体使用LinFu.DynamicProxy)解决此问题。 LinFu的文章比这篇文章更古老,所以我认为它所讨论的所有内容在被问到时都是相关的(而且今天仍然以某种方式)。我使用LinFu是因为我已经将它用于其他目的,但我确定其他一些DynamicProxy框架可用(例如Castle.DynamicProxy),或者您可以基于Reflection.Emit (不适用于性格弱的人)。它们提供了一种机制,可以充分利用AOP的大部分角色,同时保持对代码的控制。

0

您可能希望通过修改您的设计来避免重新入侵,以便在其先前的调用完成之前永远不会调用function1()。对我来说,似乎从function1()上面缺少一个图层。

相关问题