目前,我有一些看起来像这样的功能:
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 :(得分: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()上面缺少一层。