Являются ли массивы C # потокобезопасными? - PullRequest
50 голосов
/ 22 сентября 2009

В частности

  1. Создайте функцию, которая будет принимать массив и индекс в качестве параметров.
  2. Создать массив из n элементов.
  3. Создайте цикл n числа.
  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);
    }
}

Ответы [ 4 ]

44 голосов
/ 22 сентября 2009

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

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

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

24 голосов
/ 22 сентября 2009

Документация MSDN по массивам гласит:

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

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

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

Так что нет, они не безопасны для потоков.

14 голосов
/ 22 сентября 2009

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

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

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

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

5 голосов
/ 23 сентября 2009

Приведенный вами пример очень похож на то, как работают собственные параллельные расширения 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]; 
});

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...