2009-09-22 3 views
42

В частностиЯвляются ли массивы C# потоками безопасными?

  1. Создайте функцию, чтобы взять массив и индекс в качестве параметров.
  2. Создайте массив элементов n.
  3. Создайте цикл n count.
  4. Внутри цикла на новом потоке назначить новый экземпляр объекта в массив, используя индексатор, переданный в.

Я знаю, как управлять темы и т.д. Я заинтересован в курсе, если это нить безопасный способ сделать что-то.

class Program 
{ 
    // bogus object 
    class SomeObject 
    { 
     private int value1; 
     private int value2; 

     public SomeObject(int value1, int value2) 
     { 
      this.value1 = value1; 
      this.value2 = value2; 
     } 
    } 

    static void Main(string[] args) 
    { 

     var s = new SomeObject[10]; 
     var threads = Environment.ProcessorCount - 1; 
     var stp = new SmartThreadPool(1000, threads, threads); 
     for (var i = 0; i < 10; i++) 
     { 
      stp.QueueWorkItem(CreateElement, s, i); 
     } 

    } 

    static void CreateElement(SomeObject[] s, int index) 
    { 
     s[index] = new SomeObject(index, 2); 
    } 
} 
+20

Это совершенно не касается вопроса, но я бы посоветовал использовать «Environment.ProcessorCount - 1' - эти бедные одноядерные люди будут совершенно несчастны в противном случае ... –

+6

Есть люди, основных процессоров? – Gary

+4

@Gary есть виртуальные машины с «одним» процессором;) – IamIC

ответ

39

Я считаю, что если каждый поток работает только в отдельной части массива, все будет хорошо. Если вы собираетесь в поделиться данными (т. Е. Обмениваться информацией между потоками), вам понадобится какой-то барьер памяти, чтобы избежать проблем с моделью памяти.

Я верю, что если вы икру пучок нитей, каждая из которых заполнит свой участок массива, а затем ждать, пока все из этих потоков, чтобы закончить с помощью Thread.Join, что это будет делать достаточно с точки зрения барьеров для вы должны быть в безопасности. У меня нет никакой подтверждающей документации для этого на данный момент, заметьте ...

EDIT: Ваш пример кода в безопасности. Ни в коем случае два потока не обращаются к одному и тому же элементу - это как если бы каждый из них имел отдельные переменные. Тем не менее, это, как правило, не полезно само по себе. В какой-то момент, как правило, потоки захотят поделиться состоянием - один поток захочет прочитать то, что написал другой. В противном случае нет смысла писать в общий массив, а не в собственные частные переменные. Это точка, в которой вам нужно быть осторожным - координация между потоками.

+3

Элементы массива следуют тем же правилам, что и обычные переменные, поскольку обмен ссылками будет атомарным, но замена структуры, как правило, не будет. Тем не менее, это не учитывает необходимость в барьерах памяти, чтобы сделать доступ неустойчивым. Как вы сказали, безопасно ли это будет зависеть именно от того, что делает кодер. –

+0

@Steven: Да, это было о моем понимании :) –

+2

@Steven «безопасно ли это будет зависеть именно от того, что делает кодер». - Разве это не то же самое, что сказать «Нет, это не безопасно». ? – Rik

24

MSDN documentation on Arrays говорит:

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

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

Перечисление через коллекцию по существу не является потокобезопасным процедура. Даже если коллекция синхронизирована, другие потоки могут по-прежнему изменить коллекцию, из-за чего перечислитель выдает исключение. Чтобы гарантировать безопасность резьбы во время регистрации , вы можете либо заблокировать коллекцию в течение всего перечисления , либо за исключением исключений в результате изменений, внесенных другими потоками .

Нет, они не являются потокобезопасными.

+13

Это не отвечает на мой вопрос. Я не делаю никаких перечислений, как этот парень может получить 13 голосов? – Gary

+4

Голосование означает, что ответ будет полезен. Ваша галочка указывает, что ответ решает вашу проблему. Учитывая эту информацию, если я разрабатываю Threadable класс с массивами, я считаю, что первое, что я сделал бы, это запустить Reflector, открыть один из классов Framework, который использует Array в потокобезопасном контексте, и посмотреть, что они делают с SyncRoot. –

+6

Этот ответ действительно бесполезен, поскольку цитата из MSDN довольно вводит в заблуждение * и * не в точку. Доступ к массивам доступа к массиву является атомарным; даже перечисление по такому массиву (хотя, вероятно, неразумно) для этой степени является поточно-безопасным. –

11

Обычно, когда коллекция называется «не потокобезопасной», это означает, что одновременный доступ может завершиться неудачно (например, небезопасно читать первый элемент списка < T>, в то время как другой поток добавляет элемент в конце list: Список < T> может изменить размер базового массива, и доступ для чтения может перейти в новый массив до того, как данные будут скопированы в него).

Такие ошибки невозможны при использовании массивов, поскольку массивы имеют фиксированный размер и не имеют таких «структурных изменений». Массив с тремя элементами не более или менее потокобезопасен, чем три переменные.

Спецификация C# ничего не говорит об этом; но ясно, что вы знаете IL и читаете спецификацию CLI - вы можете получить управляемую ссылку (например, те, что используются для параметров C# ref) для элемента внутри массива, а затем выполнить как нормальные, так и неустойчивые нагрузки и хранилища. Спецификация CLI описывает гарантии безопасности потоков для таких нагрузок и хранилищ (например, атомарность для элементов < = 32 бит)

Так что, если я правильно задаю ваш вопрос, вы хотите заполнить массив, используя разные потоки, но присваивать каждому элементу массива только один раз? Если это так, это абсолютно безопасно для потоков.

4

Пример, который вы предоставляете, очень похож на то, как работают собственные параллельные расширения Microsoft для C# 4.0.

Этот цикл:

for (int i = 0; i < 100; i++) { 
    a[i] = a[i]*a[i]; 
} 

становится

Parallel.For(0, 100, delegate(int i) { 
    a[i] = a[i]*a[i]; 
}); 

Так что, да, ваш пример должен быть в порядке. Вот старшая blog post о новой параллельной поддержке в C#.

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