2009-12-09 3 views
5

Abbiamo avuto una piccola discussione in ufficio e non abbiamo ricevuto alcuna risposta documentata:I metodi SetValue/GetValue di System.Array sono sicuri per i thread?

È sicuro il thread System.Array.SetValue?

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

Come è possibile notare, ogni thread imposta un elemento diverso e univoco nell'array statico. Ho guardato in profondità nel codice usando reflector (e ho esaminato mscorlib.pdb). Alla fine c'è una chiamata a:

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

Quale non è documentato. Date un'occhiata alla documentazione di MSDN a System.Array in generale, e a SetValue(object, int) in particolare .. Niente su thread-safety (o forse mi manca qualcosa).

Come è espresso dalla risposta di Jon Skeet ad un similar question:

Credo che se ogni filo solo impianti su una parte separata della matrice, tutto sarà bene

I Sto cercando di ottenere una risposta definitiva a GetValue(int) e SetValue(object, int) per quanto riguarda questo problema. Qualcuno ha un link alla documentazione e/o una migliore comprensione di InternalSetValue?

+0

Una buona lettura su cosa "thread-safe" in realtà significa da Eric Lippert: http://blogs.msdn.com/ericlippert/archive/2009/10/19/what-is-this-thing-you-call -thread-safe.aspx –

+0

Nell'esempio non verrà chiamato il metodo SetValue, verrà invece emesso dal compilatore un opcode Stelem e non un opcode di chiamata. –

risposta

3

MSDN: Array class

statici pubblici (Shared in Visual Basic) di questo tipo è thread-safe. Qualsiasi membro di istanza non è garantito come thread-safe.

Questa implementazione non fornisce un wrapper sincronizzato (thread safe) per l'array ; tuttavia, le classi .NET Framework basate su Array forniscono la propria versione sincronizzata della serie tramite la proprietà SyncRoot .

Non è un thread sicuro!

Edit:

qualche informazione in più, il metodo ImpostaValore sulla classe Array non è chiamato in circostanze normali, si chiama solo quando la matrice viene utilizzato attraverso l'interfaccia IList.

il seguente codice:

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

non genererà una chiamata a SetValue(), invece di un codice operativo OpCodes.Stelem verrà generato invece.

Quindi è piuttosto irrilevante se il metodo SetValue è thread-safe o meno a meno che non si acceda all'array usando un riferimento IList.

+0

Davvero non so come mi sia mancato. Grazie! –

+1

Quindi non è thread-safe in generale, ma per quanto riguarda lo scenario specifico con ogni thread che accede solo a una parte separata dell'array? –

+0

@divo, non è garantito che sia thread-safe, vale a dire anche se l'implementazione corrente è un dettaglio di implementazione. –

0

Nell'esempio in cui ciascun thread imposta un elemento diverso nell'array, sarà sicuro. Guardalo come un frigorifero, ci sono tre lattine di birra, tre diverse vanno al frigo e scelgono il suo barattolo di birra, andrà tutto bene, lo berranno a metà strada, lo rimetteranno e torneranno più tardi .

Tuttavia, non è possibile condividere 1 lattina di birra con tre persone, né durante la programmazione né nella vita reale.

quindi sì:

se ogni filo funziona solo su una parte separata della matrice, tutto andrà

PS: ho sorgente per verificare questo, però, ma capita di uso sempre i thread e non ho mai avuto un problema di fame (?) quando ogni thread legge e scrive n in un array. Io soffro di problemi quando condivido la "stessa lattina di birra" quindi credo che la mia risposta sia corretta, ma mi piacerebbe vedere qualcuno verificarlo.

+0

Come puoi dire? Forse la classe 'System.Array' genera la propria cache su ogni chiamata setter, e questo processo non è thread-safe? –

0

Nell'esempio, le chiamate a InternalSetValue(void *, object) vengono effettuate su tre diverse posizioni di memoria. Pertanto, dovrebbe essere thread-safe. Le scritture su quelle posizioni non si perdono in altre posizioni, anche se sono membri della stessa matrice.

+0

In effetti, l'esempio di codice richiama 'InternalSetValue' tre volte con percorsi di memoria differenti, ma questo è il massimo che posso dire. Non so quale sia l'implementazione di 'InternalSetValue', e quindi non posso essere sicuro della sua sicurezza del thread. –