Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Я правильно понимаю, что вызов MemoryBarrier предотвращает возможность трансформации get_data (в процессе компиляции с оптимизацией) в код вида
Нет, Вы всё понимаете абсолютно неправильно.
MemoryBarrier не имеет (почти) никакого отношения к оптимизациям компилятора. Более того, в данном примере совершенно очевидно, что "код вида" однопоточно неэквивалентен исходному коду — бо там два обращения к m_Options против одного в оригинале. И поэтому никакая оптимизация — хоть компилятора, хоть процессора — такую трансформацию сделать не может.
Здравствуйте, drol, Вы писали:
КД>>Я правильно понимаю, что вызов MemoryBarrier предотвращает возможность трансформации get_data (в процессе компиляции с оптимизацией) в код вида
D>Нет, Вы всё понимаете абсолютно неправильно.
D>MemoryBarrier не имеет (почти) никакого отношения к оптимизациям компилятора. Более того, в данном примере совершенно очевидно, что "код вида" однопоточно неэквивалентен исходному коду — бо там два обращения к m_Options против одного в оригинале. И поэтому никакая оптимизация — хоть компилятора, хоть процессора — такую трансформацию сделать не может.
Ага. Ну вообщем, я так тоже думал... Но потом подумал — а вдруг ...
Смотрю в FW примеры с использованием Thread.MemoryBarrier() и мой моск воспринимает это именно как борьбу с "оптимизацией" в виде удаления локальных переменных
Мне, вообщем-то, нужна гарантия что значение из m_Options сначала будет прочитана в локальную переменную, а потом мы будем работать именно с этой локальной переменной.
Содержимое m_Options константно.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Например.
Долго смотрел на исходный код ConcurrentDictionary — даже баг в ReSharper'е встретил, но зачем там прикручены вызовы MemoryBarrier так и не понял.
Содержимое массива на который ссылаются this.m_buckets\buckets может изменяться на ходу, да. Но обращение к потрохам элемента — node.m_key, node.m_value, node.m_next — зависимые операции, и без предварительного чтения buckets[i] они невозможны. Зачем тут явно задавать порядок ???
Вобщем надо товарища remark'а звать.
КД>Мне, вообщем-то, нужна гарантия что значение из m_Options сначала будет прочитана в локальную переменную, а потом мы будем работать именно с этой локальной переменной.
Я полагаю, что на самом деле Вам нужно совсем другое Поэтому лучше озвучьте задачу, которую Вы пытаетесь решить.
КД>Содержимое m_Options константно.
А это неважно. Важно кто и как обращается к m_Options. Если туда из разных потоков ходят, то всё нужно очень внимательно посмотреть на предмет обеспечения корректной синхронизации.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Ну могу продублировать код еще раз
Ну а я могу Вам ещё раз повторить, что дело не в m_Options\get_data\set_data, а в том кто и как их будет использовать. И только зная сие можно понять, что должно быть написано в потрохах.
КД>Была мысль завести какую нибудь неинлайновую функцию вида КД> КД>... КД> КД>Обнаружил готовую функцию — Thread.VolatileRead(ref Object).
Вы сейчас занимаетесь разновидностью архитектурной астронавтики вперемешку с гаданием на кофейной гуще...
КД>То компилятор ругается на Interlocked.Exchange — "ссылка на изменяемое поле не будет считаться изменяемой"
Здесь это ложное срабатывание. Модификатор volatile требует от компилятора специальной схемы кодогенерации для чтения\записи поля — с отключением\ограничением оптимизаций\реордеринга, добавлением барьеров и т.п. Если же передавать поле в качестве ref-аргумента некоторому методу, то в общем случае внутри этого метода кодогенерация для такого аргумента будет обычная. Однако семантика конкретно Interlocked.Exchange() включает в себя соответствующую семантику volatile для аргументов, и посему всё нормально.