Я изучаю параллельное программирование в Java и запись моделирования для Игры Жизни.
Вот то, что я думаю:
Теперь там будет конкуренцией на общих границах сегментов. Если поток перезаписал состояние краевой ячейки, прежде чем ее сосед считал предыдущее значение, вычисление соседа будет неправильным.
Каковы мои опции?
Мой вопрос, там какой-либо другой способ иметь дело с конкуренцией в краевых ячейках, которая не включает копирование данных или более эффективна что вышеупомянутые две опции? Может использовать ReaderWriterLock, энергозависимую переменную или другой механизм синхронизации?
ОБНОВЛЕНИЕ: До сих пор решением для двойной буферизации Peter является самое чистое. Но у меня есть вопрос. Так как два массива обменяны данными, и мы не используем синхронизации (синхронизировал доступ или энергозависимую переменную), разве это не создаст проблему видимости? Действительно ли несколько центральных процессоров могли кэшировать значения массива и обновить только часть массива с каждым повторением? Затем потоки получат устаревшие значения для краевых ячеек. Действительно ли это возможно? В противном случае, почему. Если да, как я решаю его? Это кажется объявлением, что два энергозависимые массива не сделают свои отдельные элементы энергозависимыми.
Это не отвечает на ваш вопрос, но моя рекомендация будет вашим первым вариантом ... возврат новых значений вместо обновления рабочих потоков их. Я бы пошел дальше и попросил «агрегатор» объединить результаты рабочих потоков в новый массив состояний платы, а затем отбросить первый. Я считаю, что это улучшит общую читаемость логики, поскольку практически не будет «глобальных взаимодействий», о которых вам нужно беспокоиться.
При этом я немного предвзят, потому что предпочитаю программировать функционально, за исключением случаев, когда есть веская причина не делать этого.
Я предлагаю иметь 2 массива int [] []. Назовем их A и B. A будет содержать значения всех «тиков» с нечетными номерами, а B будет содержать тики с четными номерами.
Инициализируйте A в соответствии с вашим начальным состоянием. Затем позвольте вашим потокам расслабиться, чтобы вычислить следующее состояние каждой ячейки, и поместите результаты в соответствующую ячейку в B. Как только все ваши потоки будут выполнены, вы получите новое состояние в B. Теперь используйте B для вычислить следующее состояние каждой ячейки и сохранить результаты в A. В любой момент времени один массив будет доступен только для чтения, а другой - только для записи, поэтому конфликтов не будет.
Преимущества:
Недостатки:
Я бы попробовал следующий подход:
Объем памяти, необходимый для плитки nxm
, составляет 2 * (n + m - 1)
, поэтому обычно плитки большего размера (8x8) или больше) требуют пропорционально меньше памяти для кэшированных значений.
Я только что наткнулся на java.util.concurrent.Exchanger
. Он действует как точка обмена. Я, вероятно, смогу использовать его для обмена списками состояний ячеек между соседними потоками. Это было бы лучше, чем барьер, потому что мне нужно синхронизировать только между соседними рабочими.
Чтобы ответить на ваше обновление о проблеме кеширования с двойной буферизацией, это не проблема. У ЦП есть согласованный кеш, и они знают, когда данные были изменены в другом кэше ЦП.