2012-03-02 26 views
5

Estoy tratando de averiguar si Enum.TryParse de .NET 4.0 es seguro para subprocesos.Enum.TryParse - ¿es seguro para subprocesos?

El código fuente (descompilación) es:

[SecuritySafeCritical] 
public static bool TryParse<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct 
{ 
    result = default(TEnum); /// (*) 
    Enum.EnumResult enumResult = default(Enum.EnumResult); 
    enumResult.Init(false); 
    bool result2; 
    if (result2 = Enum.TryParseEnum(typeof(TEnum), value, ignoreCase, ref enumResult)) 
    { 
     result = (TEnum)enumResult.parsedEnum; 
    } 
    return result2; 
} 

Lo que parece problemático para mí es esta línea:

result = default(TEnum); /// (*) 

¿Qué pasa si otro resultado hilo visitada justo después de que ha sido establecido en el valor predeterminado y antes de que se establezca en el valor analizado?

[EDITAR] Siguiendo la respuesta de Zoidberg, me gustaría volver a formular la pregunta un poco.

La pregunta es, supongo, si Enum.TryParse es "transaccional" (o atómico).

Decir que tengo un campo estático, y pasarlo a Enum.TryParse:

public static SomeEnum MyField; 
.... 
Enum.TryParse("Value", out MyField); 

Ahora, cuando se está ejecutando TryParse, otro hilo accesos MiCampo. TryParse cambiará el valor de MyField al valor predeterminado de SomeEnum por un tiempo, y solo entonces lo establecerá en el valor analizado.

Esto no es necesariamente un error en mi código. Esperaría que Enum.TryParse establezca MyField en el valor analizado o que no lo toque en absoluto, no lo use como su campo temporal.

+0

Parece que otros métodos .NET TryParse también utilizan la variable de referencia para su propio almacenamiento temporal. ¿No debería considerarse esto un error en el código de Microsoft? Cuando solicite configurar una variable a un valor, normalmente no esperará que pase por otros estados antes de establecerse. –

+2

¿Está ** documentado ** como enhebrable? De lo contrario, ** si realmente es o no, no deberías confiar en que sea seguro para los hilos **. –

Respuesta

3

resultado (y por lo tanto la variable que se refiere) puede llegar a ser predeterminado (T), aunque el valor de cadena tiene un valor de enumeración diferente si es eso lo que quiere decir.

Prueba el siguiente programa:

public enum FooBar 
{ 
    Foo = 0, 
    Bar 
} 

internal class Program 
{ 
    private static FooBar fb = FooBar.Bar; 

    private static void Main() 
    { 
     new Thread(() => 
         { 
          while (true) 
          { 
           if (Program.fb == FooBar.Foo) // or try default(FooBar), which is the same 
           { 
            throw new Exception("Not threadsafe"); 
           } 
          } 
         }).Start(); 

     while (true) 
     { 
      if (!Enum.TryParse("Bar", true, out fb) || fb == FooBar.Foo) 
      { 
       throw new Exception("Parse error"); 
      } 
     } 
    } 
} 

Será tarde o temprano (probablemente más pronto) emitir la excepción "No-hilo".

+0

Este es exactamente mi punto. Gracias. – abieganski

7

result, como en cualquier otra variable local y parámetro, es por llamada. La seguridad de subprocesos de un parámetro by-ref es un pequeño más complejo de describir, pero: en todo uso racional, esto no será un problema. I podría forzar un escenario en el que estaba en riesgo (debido al paso de by-ref), pero sería un ejemplo artificial.

uso típico:

SomeEnumType foo; 
if(Enum.TryParse(s, true, out foo)) {...} 

es perfectamente seguro.

El siguiente es un poco más complejo:

var objWithField = new SomeType(); 
// thread 1: 
{ 
    Enum.TryParse(x, true, out objWithField.SomeField)); 
} 
// thread 2: 
{ 
    Enum.TryParse(y, true, out objWithField.SomeField)); 
} 

y no es seguro para subprocesos, pero por razones mucho más sutiles que las que se están describiendo en la pregunta.

5

Sí, lo es.

No hay ningún estado compartido en absoluto en el método que ha descompilado.

Si otro hilo llama al mismo método, obtiene su propia copia de todos los locales y result es pasado por la persona que llama.

Si result hubiera sido una variable de nivel de clase, esto habría sido un problema, pero está bien.

+3

advertencia; 'Result' es por-ref, así que mientras la copia local del puntero está aislado de hecho, el valor subyacente es por-ref y está en riesgo, pero sólo en ejemplos muy artificiales. –

+0

Entiendo tu punto. Eso sí, en este caso - 'result' es una estructura, por lo que eso significaría que no podría mutar copia de otra persona de cualquiera de todas formas no es así? –

+0

porque es por-ref ('out') es ** ** exactamente por qué nos pueden mutar versión de todos los demás: sólo hay un valor (estructuras aprobada por-ref no se copian; a * * puntero al valor se envía en su lugar) –

2

Realmente no se trata de si TryParse es seguro o no, pero si el código que lo usa (su código) es seguro para subprocesos. Si pasa la variable de resultado, y luego permite que sea accedida por otra cadena sin alguna protección de exclusión mutua, entonces podría tener un problema, pero el problema estaría en su código.

+0

Esto es lo que en realidad quería decir con mi pregunta - Voy a tratar y modificar la pregunta. Gracias. – abieganski

Cuestiones relacionadas