Запись в ту же ячейку памяти, это возможно? - PullRequest
0 голосов
/ 03 июня 2010

Рассмотрим следующее:

ThreadA и ThreadB - это два потока, записывающие диагностическую информацию в общий объект, в котором хранится список диагностической информации. Возможно ли, чтобы ThreadA и ThreadB записывали на один и тот же адрес памяти одновременно? Если так, что бы закончилось?

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

Ответы [ 6 ]

5 голосов
/ 03 июня 2010

Коррупция

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

Представьте два одновременных запроса: 1 запись и 1 чтение простого целочисленного значения. Допустим, целое число составляет 4 байта. Скажем также, что для чтения требуется 2 нс, а для записи требуется 4 нс

  • t0 , Начальное значение базового 4-байтового кортежа, [0, 0, 0, 0]
  • t1 , начинается запись операции, запись первого байта [255, 0, 0, 0]
  • t2 , запись операции продолжается, запись второго байта [255, 255, 0, 0]
  • t2 , начинается чтение операции, чтение первых 2 байтов [255, 255, -, -]
  • t3 , запись операции продолжается, запись третьего байта [255, 255, 255, 0]
  • t3 , чтение операций заканчивается, чтение последних 2 байтов [255, 255, 255, 0]
  • t4 , запись операций заканчивается, запись четвертого байта [255, 255, 255, 255]

Значение, возвращаемое чтением, не является ни оригинальным, ни новым значением. Значение полностью повреждено.

И что это значит для вас!

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

Если ваш список имеет фиксированный размер, скажем, массив ссылок на объекты, в лучшем случае вы можете потерять целые объекты, так как элементы массива перезаписываются конкурирующими потоками, в худшем случае вы обнаружите ошибку, если элемент содержит поврежденную ссылку на объект [a Сценарий коррупции выше].

Если ваш список динамический, то возможно, что основная структура данных повреждена [если массив, как в .Net List<>, когда он перераспределяется, или если связанный список ваших следующих \ prev ссылок теряется или поврежден ].

В сторону

Почему доступ к памяти не является атомарным? По той же причине реализации базового набора не являются атомарными - это было бы слишком ограничительно и приводило бы к накладным расходам, эффективно наказывая за простые сценарии. Поэтому потребителю [нам!] Предоставляется возможность синхронизировать наши обращения к памяти.

5 голосов
/ 03 июня 2010

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

0 голосов
/ 04 июня 2010

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

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

Когда нескольким потокам необходимо отправить данные в общее место назначения, может быть очень эффективно (и, как правило, независимо от языка) использовать очередь ОС (или, если она недоступна в вашей ОС, использовать канал, запись которого защищена мьютекс). Другой поток может быть заблокирован при чтении очереди, которая обрабатывает данные, когда они поступают хорошо синхронизированным способом.

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

0 голосов
/ 03 июня 2010

Одновременная запись в один и тот же глобальный адрес памяти невозможна из-за аппаратного обеспечения! Возможна фиктивная запись в том случае, если любое ядро ​​ЦП имеет собственный кэш, но результат непредсказуем, из-за этого придумана инструкция «LOCK», которая предоставляет беспрепятственный или атомарный доступ к глобальному адресу памяти.

0 голосов
/ 03 июня 2010

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

0 голосов
/ 03 июня 2010

В своем вопросе вы ссылаетесь как на объект, так и на область памяти. Я предполагаю, что вы имеете в виду расположение в памяти, поскольку объекты могут действовать по-другому (выделять новую память или что-то в этом роде). Вы можете сделать так, чтобы оба потока записывали на один и тот же адрес, но результаты не предсказуемы. Если вы выполняете оба потока и смотрите на результат после вызова, вы не можете рассчитывать на определенный результат. Если вам нужно сделать это, вы должны защитить операцию записи с помощью мьютекса или критического раздела или чего-либо еще. Если это объект, объект может защищать себя, чтобы быть потокобезопасным. Трудно сказать без подробностей ...

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