Доступ к массиву из нескольких потоков - PullRequest
5 голосов
/ 19 февраля 2010

Допустим, у меня есть два массива:

int[] array1 = new int[2000000];
int[] array2 = new int[2000000];

Я вставляю некоторые значения в массивы, а затем хочу добавить содержимое массива2 в массив1 следующим образом:

for(int i = 0; i < 2000000; ++i) array1[i] += array2[i];

Теперь, допустим, я хочу ускорить обработку на многопроцессорной машине, поэтому вместо простого цикла, как описано выше, я создаю два потока. Один из которых я обработал первые 1000000 элементов в массиве, другой я обработал последние 1000000 элементов в массиве. Мой основной поток ждет, пока эти два потока уведомят его о завершении, а затем продолжит использовать значения из array1 для всех важных вещей. (Обратите внимание, что два рабочих потока не могут быть прерваны и могут быть использованы повторно, но основной поток не возобновит работу, пока они оба не уведомят его об этом.)

Итак, мой вопрос: как я могу быть уверен, что основной поток увидит изменения, внесенные двумя рабочими потоками в массив? Могу ли я рассчитывать на то, что это произойдет, или мне нужно пройти какую-то специальную процедуру, чтобы убедиться, что рабочие потоки сбрасывают свои записи в массив, а основной поток отбрасывает значения своего кэшированного массива?

Ответы [ 6 ]

6 голосов
/ 19 февраля 2010

Если вам повезло и у вас есть возможность использовать .NET 4.0, тогда вы можете просто написать:

Parallel.For(0, 2000000, i => { array1[i] += array2[i]; });

Вам не нужна явная блокировка или синхронизация, потому что:

  • Каждая задача (выполнение тела цикла for) воздействует на непересекающуюся часть массива
  • Parallel.For, пока все задачи не завершатся, прежде чем она вернется, поэтому будет неявный барьер памяти.1011 *
2 голосов
/ 19 февраля 2010

Как я могу быть уверен, что основной поток увидит изменения, внесенные двумя рабочими потоками в массив? Могу ли я рассчитывать на то, что это произойдет, или мне нужно пройти через специальную процедуру, чтобы убедиться, что рабочие потоки сбрасывают свои записи в массив, а основной поток отбрасывает значения своего кэшированного массива?

Здесь вам не нужно никакой специальной обработки - вы всегда будете работать с одними и теми же объектами в памяти.

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

Однако, если вы делаете только простое добавление, накладные расходы на многопоточность и синхронизацию с основным потоком ~ могут ~ перевесить полученные преимущества ... Если вы сделаете это, профилируйте, чтобы убедиться, что он предоставляет сеть -Получает.

2 голосов
/ 19 февраля 2010

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

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

Если вам нужен явный барьер, используйте Thread.MemoryBarrie r.

1 голос
/ 19 февраля 2010

Если вы разбиваете индексный диапазон на неперекрывающиеся диапазоны, как вы предлагали, то, если массив создается в разделяемой памяти (т.е. не каждым потоком), тогда блокировки не требуются.

0 голосов
/ 19 февраля 2010

Пока вы не сделали копию массива в основном потоке, я не думаю, что вам нужно что-то делать. Просто дождитесь окончания рабочих потоков.

0 голосов
/ 19 февраля 2010

С вами, вероятно, все будет в порядке, если вы будете использовать обратные вызовы, когда они закончат изменение массивов, но если возникнут какие-либо вопросы, использование блокировки обеспечит освобождение других потоков от массивов.

lock (array1)
{
}

http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.71).aspx

...