2010-02-28 10 views
8

¿Es una estructura C# segura para subprocesos?¿Las segmentaciones de C# son seguras?

Por ejemplo, si hay una:

struct Data 
{ 
    int _number; 
    public int Number { get { return _number; } set { _number = value; } } 

    public Data(int number) { _number = number; } 
} 

en otro tipo:

class DadData 
{ 
    public Data TheData { get; set; } 
} 

es propiedad denominada theData, flujos seguros?

+0

Depende. ¿Qué estás haciendo con la estructura? – SLaks

+2

Además, no debe hacer una estructura mutable. http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil – SLaks

+0

De acuerdo con este http://stackoverflow.com/questions/2353014/arec-c-structs-thread-safe/2353051#2353051 debieras. –

Respuesta

8

No, las estructuras en .NET no son intrínsecamente seguras para subprocesos.

Sin embargo, la semántica de copia por valor que las estructuras tienen una gran relevancia para esta convección.

Si está transfiriendo sus estructuras y asignándolas de alguna forma a variables o parámetros de paso por valor (sin palabras clave de ref o de salida), se está utilizando una copia .

Por supuesto, esto significa que los cambios realizados en la copia no se reflejan en la estructura original, pero es algo a tener en cuenta al pasarlos.

Si accede directamente a la estructura de manera que no implique la semántica copia-por-valor (por ejemplo, acceso a un campo estático que es el tipo de la estructura, y como Marc Gravel points out in his answer, hay muchas otras maneras) a través múltiples hilos, entonces debes tener en cuenta la seguridad de la hebra de la instancia.

+0

Y para empeorar esto, también depende de * gran * oferta si su código se refiere a un * campo *, una * variable * o una * propiedad *. Afortunadamente, en este caso, la propiedad implementada automáticamente ('TheData') elimina la mayoría de estos. Así que solo lo estoy mencionando para que esté completo ;-p –

+0

Gracias casperOne! ¡Gracias, Marc! Como dijo CasperOne, pensé "la semántica de copia por valor que las estructuras tienen una gran relevancia para esta conversación"; sin embargo, no se pudo ver ninguna instrucción sobre refrences. Este código no es mi código real, sino una representación. Estoy escribiendo algunas aplicaciones de subprocesos múltiples; y algunas herramientas comunes de programación en paralelo son C#; porque algunos patrones están volviendo con frecuencia;) (Seguro que buscaré críticas aquí cuando haya salido algo valioso). –

-2

No. ¿Por qué sería seguro para subprocesos? Solo son datos. No se convierte en hilo seguro por magia.

1

A struct no es más seguro para subprocesos que un campo o variable común. Si tiene al menos un hilo que lo modifica y al menos un hilo más lo toca de cualquier manera al mismo tiempo, puede terminar con un comportamiento inesperado/indefinido.

Además, las estructuras mutables son olores de código. ¿Hay alguna razón particular por la que necesita que sea struct en lugar de class? ¿Necesita semántica de tipo de valor para estos datos?

9

Bueno, la mejor práctica es que las estructuras siempre deben ser inmutables (excepto en algunos escenarios muy específicos, e incluso en riesgo). Y la información inmutable siempre es segura para hilos. Entonces, si siguió las mejores prácticas e hizo esto:

struct Data 
{ 
    readonly int _number; 
    public int Number { get { return _number; } } 

    public Data(int number) { _number = number; } 
} 

then yes; eso es seguro para subprocesos En todos los demás casos, la respuesta es "probablemente no".

Tenga en cuenta también que las reglas se aplican atomicidad, por lo que incluso una sola lectura o actualización de DadData.TheData no se puede suponer a ser flujos seguros, incluso con una estructura inmutable. Usted podría (especialmente para las estructuras sobredimensionadas) tener un hilo leyendo la estructura, mientras que otro hilo lo vuelve a escribir; sin sincronización, sucederán cosas malas (eventualmente).

0

Las lecturas y escrituras directas de diferentes hilos de diferentes miembros de una estructura mutable no interferirán entre sí. El acceso de diferentes subprocesos al mismo miembro a través de métodos interbloqueados se comportará de acuerdo con la semántica de esos métodos. Estos hechos pueden permitir estructuras mutables para permitir un comportamiento seguro de subprocesos.

Ubicaciones de almacenamiento mutables que contienen estructuras que no ofrecen ningún medio de mutación, excepto reemplazo absoluto no ofrecen seguridad de hilos, excepto que en casos donde una estructura contiene un entero de 32 bits o una referencia de objeto individual, un intento de leer una ubicación de almacenamiento de estructuras (de un solo elemento) al mismo tiempo que se está escribiendo está garantizada para leer datos totalmente antiguos o completamente nuevos. Tenga en cuenta que no es posible utilizar ninguno de los métodos Interbloqueados con estructuras inmutables, incluso las estructuras que contienen un solo entero o referencia de objeto.

0

No, no lo son. Creé una aplicación muy simple para ver si 10/10 subprocesos de productor/consumidor tienen acceso a la misma variable de estructura. Y, finalmente, verá Debugger.Break(); será golpeado El saldo bancario nunca debe ir por debajo del valor 0.

namespace StructThreadSafe 
{ 
    class Program 
    { 
     struct BankBalance 
     { 
      public decimal Balance { get; set; } 
     } 

     static void Main(string[] args) 
     { 
      BankBalance bankBalance = new BankBalance(); 
      bankBalance.Balance = 100; 
      List<Task> allTasks = new List<Task>(); 
      for (int q = 0; q < 10; q++) 
      { 
       Task producer = new Task(() => 
       { 
        for (int i = 0; i < 1000; i++) 
        { 
         if (bankBalance.Balance < 0) 
         { 
          if (Debugger.IsAttached) 
          { 
           Debugger.Break(); 
          } 
         } 
         bankBalance.Balance += 5; 
         Console.WriteLine("++Current Balance: " + bankBalance.Balance); 
         System.Threading.Thread.Sleep(100); 
        } 
       }); 
       allTasks.Add(producer); 
      } 
      for (int w = 0; w < 10; w++) 
      { 
       Task consumer = new Task(() => 
       { 
        for (int i = 0; i < 1000; i++) 
        { 
         if (bankBalance.Balance < 0) 
         { 
          if (Debugger.IsAttached) 
          { 
           Debugger.Break(); 
          } 
         } 
         if (bankBalance.Balance > 15) 
         { 
          bankBalance.Balance -= 15; 
          Console.WriteLine("--Current Balance: " + bankBalance.Balance); 
         } 
         else 
         { 
          Console.WriteLine("**Current Balance below minimum: " + bankBalance.Balance); 
         } 
         System.Threading.Thread.Sleep(100); 
        } 
       }); 
       allTasks.Add(consumer); 
      } 
      allTasks.ForEach(p => p.Start()); 
      Task.WaitAll(allTasks.ToArray()); 

     } 
    } 
} 
Cuestiones relacionadas