2009-12-09 17 views
5

Tuvimos una pequeña discusión en la oficina y no obtuvimos respuesta documentada:¿Son los métodos SetValue/GetValue de System.Array thread-safe?

¿System.Array.SetValue es seguro?

using System; 
using System.Text; 
using System.Threading; 

namespace MyApp 
{ 
    class Program 
    { 
     private static readonly object[] arr = new object[3]; 

     static void Main(string[] args) 
     { 
      string value1 = "hello"; 
      int value2 = 123; 
      StringBuilder value3 = new StringBuilder(); 
      value3.Append("this"); 
      value3.Append(" is "); 
      value3.Append("from the StringBuilder"); 

      var states = new object[] 
          { 
           new object[] {0, value1}, 
           new object[] {1, value2}, 
           new object[] {2, value3} 
          }; 

      ThreadPool.QueueUserWorkItem(MySetValue, states[0]); 
      ThreadPool.QueueUserWorkItem(MySetValue, states[1]); 
      ThreadPool.QueueUserWorkItem(MySetValue, states[2]); 
      Thread.Sleep(0); 

      Console.WriteLine("press enter to continue"); 
      Console.ReadLine(); 

      // print the result 
      Console.WriteLine("result:"); 
      for (int i = 0; i < arr.Length; i++) 
      { 
       Console.WriteLine("arr[{0}] = {1}", i, arr[i]); 
      } 

      // quit 
      Console.WriteLine("press enter to quit"); 
      Console.ReadLine(); 

     } 

     // callback 
     private static void MySetValue(object state) 
     { 
      var args = (object[]) state; 
      var index = (int)args[0]; 
      var value = args[1]; 
      arr[index] = value; // THREAD-SAFE ?? 
     } 
    } 
} 

Como puede ver, cada hilo establece un elemento diferente y único en la matriz estática. Miré profundamente en el código usando un reflector (y miré en mscorlib.pdb). Finalmente hay una llamada a:

[MethodImplAttribute(MethodImplOptions.InternalCall)] 
private unsafe extern static void InternalSetValue(void * target, Object value); 

Lo que no está documentado. Eche un vistazo a la documentación de MSDN al System.Array en general, y al SetValue(object, int) en particular .. Nada sobre seguridad de hilos (o tal vez me falta algo).

Como se expresó por la respuesta de Jon Skeet a un similar question:

Creo que si cada hilo sólo obras en una parte separada de la matriz, todo estará bien

I Estoy tratando de obtener una respuesta definitiva a GetValue(int) y SetValue(object, int) con respecto a este tema. ¿Alguien tiene un enlace de documentación y/o una mejor comprensión de InternalSetValue?

+0

Una buena lectura acerca de lo que "thread-safe" significa en realidad por Eric Lippert: http://blogs.msdn.com/ericlippert/archive/2009/10/19/what-is-this-thing-you-call -thread-safe.aspx –

+0

En su ejemplo no se llamará el método FijarValor, en lugar de un código de operación Stelem será emitida por el compilador, y no un código de operación llamada. –

Respuesta

3

MSDN: Array class

estáticos públicos (Shared en Visual Basic) miembros de este tipo es seguro para subprocesos. Cualquier miembro de instancia no es garantizado para ser seguro para subprocesos.

Esta implementación no proporciona una envoltura sincronizada (hilo seguro) para una matriz; sin embargo, las clases de .NET Framework basadas en Array proporcionan su propia versión sincronizada de la colección utilizando la propiedad SyncRoot .

No es hilo de seguridad!

Editar:

Algo de información adicional, la FijarValor método en la clase Array no se llama en circunstancias normales, sólo se llama cuando se usa la matriz a través de la interfaz IList.

el siguiente código:

int[] arr = ... 
arr[i] = value; 

no generará una llamada a FijarValor(), en lugar de un código de operación OpCodes.Stelem se generará en su lugar.

Por lo tanto, es bastante irrelevante si el método EstablecerValor es hilo seguro o no a menos que se accede a la matriz mediante una referencia IList.

+0

Realmente no sé cómo me lo perdí. ¡Gracias! –

+1

Por lo tanto, no es seguro para subprocesos en general, pero ¿qué hay del escenario específico con cada subproceso que accede solo a una parte separada de la matriz? –

+0

@divo, no se garantiza que sea seguro para subprocesos, lo que significa que incluso si la implementación actual es un detalle de implementación. –

0

En su ejemplo, donde cada subproceso establece un elemento diferente en la matriz, será seguro para subprocesos. Véalo como una nevera, hay tres latas de cerveza allí, tres diferentes van al refrigerador y recogen su lata de cerveza, todo irá bien, lo beberán a mitad de camino, lo devolverán y lo devolverán más tarde .

Sin embargo, no puede compartir 1 lata de cerveza con tres personas, ni en la programación ni en la vida real.

así que sí:

si cada hilo sólo funciona en una parte separada de la matriz, todo estará bien

PS: No tengo ninguna fuente para verificar esto, sin embargo, pero resulta que utilizar hilos todo el tiempo y nunca tuve un problema de inanición (?) cuando cada hilo lee 'n escribe simultáneamente en una matriz. SÍ tengo problemas cuando comparto la "misma lata de cerveza", por lo tanto, creo que mi respuesta es correcta, pero me gustaría que alguien verifique esto.

+0

¿Cómo puedes saberlo? ¿Tal vez la clase 'System.Array' genera su propio caché en cada invocación de setter, y este proceso no es seguro para subprocesos? –

0

En su ejemplo, se están haciendo las llamadas a InternalSetValue(void *, object) a tres diferentes posiciones de memoria. Por lo tanto, debe ser seguro para subprocesos. Las escrituras en esos lugares no van a desangrarse en otras ubicaciones, incluso si son miembros de la misma matriz.

+0

De hecho, el ejemplo de código invoca 'InternalSetValue' tres veces con diferentes ubicaciones de la memoria de los árboles, pero eso es todo lo que puedo decir. No sé cuál es la implementación de 'InternalSetValue', y por lo tanto, no puedo estar seguro de su seguridad de subprocesos. –

Cuestiones relacionadas