2009-12-09 4 views
5

Мы провели небольшую дискуссию в офисе и не получили документально подтвержденного ответа:Являются ли методы SetValue/GetValue System.Array потокобезопасными?

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

Как вы можете видеть, каждая нить устанавливает другой, уникальный элемент в статическом массиве. Я глубоко заглянул в код, используя отражатель (и посмотрел mscorlib.pdb). В конце концов звонит:

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

Это не задокументировано. Взгляните на документацию MSDN на System.Array в целом и на SetValue(object, int) в частности. Ничего о потокобезопасности (или, может быть, я чего-то не хватает).

Как это выражается ответ Джона Скита к similar question:

Я считаю, что если каждая нить только работает на отдельной части массива, все будет хорошо

I Я пытаюсь получить конкретный ответ GetValue(int) и SetValue(object, int) относительно этого вопроса. У кого-то есть ссылка на документацию и/или лучшее понимание InternalSetValue?

+0

Хорошо читайте о том, что на самом деле означает «поточно-безопасный» Эрик Липперт: http://blogs.msdn.com/ericlippert/archive/2009/10/19/what-is-this-thing-you-call -thread-safe.aspx –

+0

В вашем примере метод SetValue не будет вызываться, вместо этого код команды Stelem будет испускаться компилятором, а не код вызова вызова. –

ответ

3

MSDN: Array class

Открытый статический (Shared в Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не являются гарантированно надежными потоками.

Эта реализация не обеспечивает синхронизацию (потокобезопасную) для a Array; однако классы .NET Framework на основе массива предоставляют свою собственную синхронизированную версию с использованием свойства SyncRoot .

Это не потолочный сейф!

Edit:

Некоторой дополнительная информация, метод SetValue от класса Array, не вызывается в нормальных условиях, это только называется, когда используются массив через IList интерфейс.

следующий код:

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

не будет генерировать вызов SetValue(), вместо этого OpCodes.Stelem опкод будет генерироваться вместо этого.

Так что это не имеет значения, если метод SetValue является потокобезопасным или нет, если только массив не обращается с использованием ссылки IList.

+0

Я действительно не знаю, как я это пропустил. Благодаря! –

+1

Таким образом, он не является потокобезопасным в целом, но как насчет конкретного сценария с каждым потоком, обращающимся только к отдельной части массива? –

+0

@divo, не гарантируется, что он безопасен по потоку, что означает, что даже если текущая реализация - это деталь реализации. –

0

В вашем примере, где каждый поток задает другой элемент в массиве, он будет потокобезопасным. Посмотрите на холодильник, там есть три банки с пивом, три разных входа в холодильник и забрать свою банку с пивом, все будет хорошо, они выпивают ее на полпути, вернут и вернутся к ней позже ,

Однако вы не можете разделить 1 банку пива с тремя людьми, ни при программировании, ни в реальной жизни.

так что да:

если каждый поток работает только на отдельной части массива, все будет хорошо

PS: У меня нет источника, чтобы проверить это, хотя, но случается использовать потоки все время, и у меня никогда не было проблемы с голодом (?), когда каждый поток читает «n пишет одновременно в массиве. У меня проблемы с совместным использованием «пивной банки», поэтому я считаю, что мой ответ верен, но я хотел бы, чтобы кто-то подтвердил это.

+0

Как вы можете сказать? Может быть, класс 'System.Array' генерирует свой собственный кеш при каждом вызове setter, и этот процесс не является потокобезопасным? –

0

В вашем примере вызовы InternalSetValue(void *, object) выполняются в трех разных ячейках памяти. Поэтому он должен быть потокобезопасным. Записи в этих местах не истекают в другие места, даже если они являются членами одного и того же массива.

+0

Действительно, пример кода вызывает «InternalSetValue» три раза с деревом разных мест памяти, но это насколько я могу судить. Я не знаю, что такое реализация 'InternalSetValue', и поэтому я не могу быть уверен в ее безопасности потоков. –

 Смежные вопросы

  • Нет связанных вопросов^_^