Подтвердить что ты не робот

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

В частности,

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

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

 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);
    }
}
4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Итак, да, ваш пример должен быть в порядке. Здесь более старое сообщение о новой параллельной поддержке в С#.