2008-11-24 5 views
6

Por el momento, tengo algunas funciones que tener este aspecto:¿Puedo agregar un atributo a una función para evitar el reingreso?

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

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

    inFunction1 = false; 
} 

Me gustaría ser capaz de declarar como esto:

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

¿Hay un atributo que puedo añadir a una función para evitar la reentrada? Si no, ¿cómo podría hacer una? He oído acerca de los atributos AOP que se pueden usar para agregar código antes y después de las llamadas a funciones; Serían adecuados?

+0

¿Cuál es el alcance de esta restricción en presencia de varios subprocesos y varias instancias de objeto? ¿Es que la función1 se puede ejecutar en cualquier momento solo por un hilo para la instancia de un solo objeto o es más relajada? – Constantin

Respuesta

1

No existe dicho atributo predefinido. Puedes crear nuevos atributos, pero eso no te ayudará. El problema es hacer que el atributo personalizado impida que se vuelva a llamar al método, lo que no creo posible.

La instrucción de bloqueo no es lo que desea, ya que eso hará que las llamadas se bloqueen y esperen, no se devolverán de inmediato.

PD: use un intento ... finalmente bloquear en la muestra anterior. De lo contrario, si se lanza una excepción en el medio de la función, inFunction1 se dejará en verdadero y todas las llamadas volverán inmediatamente.

p. Ej. :

if (inFunction1) 
    return; 

try 
{ 
    inFunction1 = true; 

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

No creo que sea posible.

Lo más cercano será el atributo 'Sincronizado', pero eso bloqueará todas las llamadas posteriores.

2

Puede encontrar que puede usar PostSharp para lograr esto, junto con las sugerencias de Anthony sobre el uso de try/finally. Sin embargo, es probable que sea complicado. También considere si desea que la reentrada sea por subproceso o por instancia. (¿Pueden varios hilos llamar al método para comenzar, o no?)

No hay nada como esto en el marco mismo.

1

Si esto es por la seguridad del subproceso, debe tener cuidado con esa variable.

Es posible que otro hilo pueda entrar en la función y pasar la verificación antes de que el primer hilo haya establecido la variable.

asegurarse de que es volátil marcada así:

private volatile bool inFunction1 = false; 
+3

"volátil" no solucionará esta condición de carrera. – Constantin

5

sin montaje e IL volver a escribir, no hay manera para que usted pueda crear un atributo personalizado que modifica el código en la forma en que usted describe.

Sugiero que utilice un enfoque basado en delegado, por ejemplo, para las funciones de un solo argumento:

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

Este método tiene la función de envolver (suponiendo que coincide Func < Targ, T> - puede escribir otras variantes, o una versión totalmente genérico con más esfuerzo) y un suplente función para llamar en casos de reingreso. (La función alternativa podría arrojar una excepción, o regresar inmediatamente, etc.) Luego, a lo largo de su código, donde normalmente estaría llamando al método aprobado, llame al delegado devuelto por WrapAgainstReentry() en su lugar.

+0

Esto todavía presenta problemas inherentes en entornos de múltiples hilos. Ver mi publicación sobre variables volátiles arriba. –

+0

Por supuesto, Rob. Es solo una demostración, ya que solo maneja una firma de función muy específica. –

14

En lugar de utilizar un bool y se establece directamente, trate de usar un largo y la clase 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 
} 

Esto hará que el hilo de verificación seguro.

+1

Por curiosidad, ¿hay alguna razón particular para usar 'long' en lugar de' int' aquí? (También hay una sobrecarga 'CompareExchange (ref int, int, int)') – FunctorSalad

+2

Sin motivo en particular. Uso una gran cantidad de programación de Win32 y las funciones interconectadas tardan mucho. Usar un int no hará ninguna diferencia en el ejemplo anterior. – Sean

+0

Usted sabe que en C# un largo se define como int64, ¿no? – Theraot

4

Se puede construir un atributo PostSharp comprobar para ver si el nombre del método es en el seguimiento de la pila actual

[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; 
    } 
+0

Eso solo funcionaría si la reentrada se produce en el mismo hilo, pero de todos modos, parece interesante. – Simon

+0

Solo consideraría la reentrada en el mismo hilo. Si la reentrada significa a través de subprocesos, ¿qué pasa con los dominios de la aplicación? – Bob

1

Este hilo es un poco viejo pero pensé que valdría la pena ponerla en 2012 porque este problema todavía existe (más o menos). Pude resolver este problema usando objetos proxy generados usando Reflection.Emit (específicamente usando LinFu.DynamicProxy). El artículo de LinFu es anterior a este artículo, por lo que supongo que todo lo que se discutió fue relevante cuando se lo preguntaron (y aún de alguna manera todavía hoy).

Utilicé LinFu porque ya lo estaba utilizando para otros fines, pero estoy seguro de que algunos de los otros frameworks DynamicProxy disponibles funcionarían para usted (por ejemplo, Castle.DynamicProxy) o podría hacer los suyos propios basados ​​en Reflection.Emit (no para aquellos con disposiciones débiles). Proporcionan un mecanismo que ocupa una gran parte de la función de AOP mientras te mantiene en control de tu código.

0

Es posible que desee considerar evitar la reentrada modificando su diseño para que nunca llame a function1() antes de que se complete su invocación anterior. Para mí, parece que falta una capa desde arriba function1().

+0

Ok @Rob. Puedo apreciar eso. – Chris

Cuestiones relacionadas