Неиспользованное порванное чтение - неопределенное поведение?
Вопрос:
Существуют шаблоны (например, здесь C#/CLR: MemoryBarrier и рваные операции чтения), которые могут выполнять рваные операции чтения, но никогда не использовать результирующее значение, если могло произойти роковое чтение.
Это неопределенное поведение в C#?
Связанный: Как я мог определить для себя, является ли это неопределенным поведением или нет?
Неудачная попытка определить ответ:
Насколько я понимаю (исправьте мое, если я ошибаюсь), что в C++11 это было бы неопределенным поведением, потому что оно не определено моделью памяти, которая была добавлена в C++11 (в более старых версиях все многопоточность зависела от реализации поведение). Теоретически я мог бы определить это из спецификации.
Я пытался сделать это для C# и не удалось. Я не мог найти модель памяти в спецификации. Ниже приведены некоторые заметки из моего неудачного путешествия по спецификации языка C# ( http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf), где я пытаюсь отследить семантику назначения одной структуры к другому в случае, когда может быть гонка данных (один читатель копирует, один писатель пишет в копируемые данные). Мне не удалось найти даже семантику для назначения или чтения из любой переменной в любом сценарии. Либо я что-то пропустил (почти наверняка), либо использование переменных - неопределенное поведение.
Мои заметки при чтении соответствующих частей спецификации C#:
Спецификация языка C# говорит, что типы являются атомарными, но никогда не могут считывать и записывать атомарные и неатомарные:
12.5 Атомарность ссылок на переменные
Чтения и записи следующих типов данных должны быть атомарными: типы bool, char, byte, sbyte, short, ushort, uint, int, float и reference. Кроме того, чтение и запись перечислимых типов с базовым типом в предыдущем списке также должны быть атомарными. Чтение и запись других типов, включая long, ulong, double и decimal, а также определяемые пользователем типы, не обязательно должны быть атомарными. Помимо библиотечных функций, разработанных для этой цели, нет никакой гарантии атомарного чтения-изменения-записи, например, в случае увеличения или уменьшения.
Он не говорит, подразумевает ли "атомарность" (которую он не определяет), здесь какие-либо ограждения (так что я предполагаю, что нет), но более уместно для этого вопроса, он даже не определяет, что делают чтение и запись (что нетривиально в многопоточные программы).
Ищу 14.14.1 Simple assignment
, это, кажется, степень спецификации записи (объясняя "x = y"):
Значение, полученное в результате оценки и преобразования y, сохраняется в местоположении, заданном оценкой x.
8.3 Variables and parameters
состояния:
Переменная должна быть назначена до того, как ее значение может быть получено.
но не определяет получение значения. Нет, где я вижу спецификацию для того, что производит чтение (можно предположить, что последнее, что вы написали в однопоточном случае, поэтому я не могу найти это в спецификации).
10.10 Execution order
кажется, что излишне ограничивать (по сравнению с обычной семантикой получения и выпуска, используемой в статье MSDN, приведенной ниже) изменчивость по отношению к записи (никакие записи не могут перемещаться в любом направлении по отношению к ссылке на volatile), в то время как при ограничении чтения (они могут перемещаться в обоих направление между изменчивыми операциями. Также не упоминается Thread.MemoryBarrier (документация которого, по-видимому, запрещает переупорядочивание процессора, но не переупорядочивает компилятор, поэтому он слишком слаб). Он также не ссылается на то, что означает загрузка из переменной / поля, так что много неприятного оффтопа вопросов нет, но ответов нет.
Я прочитал все части спецификации, которые я мог найти, которые имеют отношение. Нигде не выполняется чтение из поля / памяти / переменной (похоже, "переменная" - это правильный термин здесь).
Может быть, где-то в спецификации языка есть спецификация поведения чтения / загрузки и записи / хранения переменных (иначе: модель памяти), но если есть (я не смог ее найти), она не ссылается на "атомарный Msgstr "(Я искал это для атомарного: раздел 12.5 - единственное использование). Я не понимаю, как какой-либо код C# может быть определен, поэтому ясно, что я что-то упускаю: я не думаю, что действительная реализация C# могла бы просто завершиться (из-за того, что она была неопределенным поведением!) При любом чтении или записи значение.
Если многопоточный (и, возможно, даже однопоточный) C# на самом деле ужасно недооценен без модели памяти, есть ли место для просмотра спецификаций для конкретных реализаций? Пример: Если C# не определяет семантику чтения и записи, возможно, различные компиляторы Microsoft C# (сейчас их как минимум 3) предоставляют спецификации, а также Mono? Безопасно ли это в любой из реализаций и что это хороший способ сказать?
Может быть, есть какие-то неформальные (не в спецификации) правила, которые считаются безопасными (все основные реализации соответствуют)? Это было бы страшно, но если это все, что мы получим, я возьму это.
Некоторые актуальные, но недостаточные источники:
Я обнаружил, что эта статья утверждает, что она относится к модели памяти C#, но она зависит от конкретной реализации (относится к CLR) и не охватывает рассматриваемый случай. Это также дает хорошее ясное объяснение того, что я хотел бы, чтобы изменчивая семантика была (стиль C++11 приобретает релиз), но сильнее в некоторых отношениях и слабее в других, чем 10.10 Execution order
из спецификации языка, так что я думаю, что это неправильно: https://msdn.microsoft.com/magazine/jj863136
В этой статье, похоже, содержится много полезной информации, сравнивающей модель памяти C# с C++11, но также, насколько я могу судить, эта проблема не рассматривается: http://blog.alexrp.com/2014/03/30/dot-net-atomics-and-memory-model-semantics/
Хорошая статья о переупорядочении вопросов. Обновление в конце упоминает, что "изменчивое чтение может быть перемещено назад во времени относительно изменчивой записи", что не соответствует 10.10 Execution order
из спецификации, но согласен с тем, что большинство людей видят в том, что семантика должна быть (то есть она согласуется со статьей MSDN и классом Volatile, но не ключевым словом, насколько я могу судить): http://blog.coverity.com/2014/03/12/can-skip-lock-reading-integer/
Вкратце, эта статья в основном посвящена изменчивости и тому, как она предотвращает избыточное устранение нагрузки и почему это необходимо: https://blogs.msdn.microsoft.com/ericlippert/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three/