Предотвращает ли volatile вводимые операции чтения или записи? - PullRequest
0 голосов
/ 31 октября 2018

В C # ключевое слово volatile гарантирует, что чтение и запись имеют семантику получения и выпуска соответственно. Тем не менее, говорит ли он что-нибудь о введенных операциях чтения или записи?

Например:

volatile Thing something;
volatile int aNumber;

void Method()
{
    // Are these lines...
    var local = something;
    if (local != null)
        local.DoThings();

    // ...guaranteed not to be transformed into these by compiler, jitter or processor?
    if (something != null)
        something.DoThings(); // <-- Second read!



    // Are these lines...
    if (aNumber == 0)
        aNumber = 1;

    // ...guaranteed not to be transformed into these by compiler, jitter or processor?
    var temp = aNumber;
    if (temp == 0)
        temp = 1;
    aNumber = temp; // <-- An out-of-thin-air write!
}

Ответы [ 3 ]

0 голосов
/ 31 октября 2018

Интересно, вы неправильно поняли, что означает volatile. Volatile можно использовать с типами, которые можно читать или записывать как атомарное действие.

Нет захвата / выпуска блокировки , только барьер для перекомпоновки во время компиляции и во время выполнения для обеспечения семантики aquire / release без блокировки (https://preshing.com/20120913/acquire-and-release-semantics/). На не x86 это может потребовать указания барьера в ассемблере, но не взломать замок.

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


Ваш вопрос немного двусмысленный.

1 / Если вы имеете в виду, преобразует ли компилятор:

var local = something;
if (local != null) local.DoThings();

в

if (something != null) something.DoThings();

тогда ответ - нет.

2 / Если вы имеете в виду, будет ли дважды вызываться «DoThings()» для одного и того же объекта:

var local = something;
if (local != null) local.DoThings();
if (something != null) something.DoThings(); 

тогда ответ в основном да, если только другой поток не изменил значение "something" до вызова второго "DoThings()". Если это так, то это может привести к ошибке времени выполнения - если после оценки условия «if» и до вызова «DoThings» другой поток устанавливает для «something» значение null, то вы получит ошибку во время выполнения. Я предполагаю, что именно поэтому у вас есть "var local = something;".

3 / Если вы имеете в виду, будет ли следующее чтение двух:

if (something != null) something.DoThings();

тогда да, одно чтение условия и второе чтение, когда оно вызывает DoThings() (при условии, что something не равно нулю). Если бы он не был помечен volatile, компилятор мог бы справиться с этим за одно чтение.

В любом случае реализация функции "DoThings()" должна знать, что она может вызываться несколькими потоками, поэтому необходимо рассмотреть возможность включения комбинации блокировок и ее собственных изменяемых членов.

0 голосов
/ 03 ноября 2018

Эта формулировка из спецификации C #:

Порядок побочных эффектов сохраняется относительно летучих читает и пишет ...

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

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

В этом Q / A обсуждается, как предотвратить введение чтения (но без обсуждения введения записи).

В комментариях к этой статье два сотрудника Microsoft (по крайней мере на момент написания комментариев) прямо заявляют, что чтение и запись введений volatile переменных не допускаются.

Стивен Туб

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

Игорь Островский

В другом месте в спецификации C # энергозависимое чтение определено как "побочный эффект". В результате повторное чтение m_paused будет эквивалентно добавлению другого побочного эффекта, что недопустимо.

Я думаю, что из этих комментариев мы можем сделать вывод, что введение побочного эффекта в C #, любого вида побочного эффекта, в любом месте кода не допускается.

Соответствующая цитата из стандарта CLI гласит следующее в разделе I.12.6.7:

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

Насколько я знаю, CLI явно не говорит о введении новых побочных эффектов.

0 голосов
/ 31 октября 2018

Вот что говорит C # spec 1 о Порядке исполнения :

Выполнение программы на C # происходит таким образом, что побочные эффекты каждого выполняющегося потока сохраняются в критических точках выполнения. побочный эффект определяется как чтение или запись изменчивого поля ...

Среда выполнения может свободно изменять порядок выполнения программы на C # с учетом следующих ограничений:

...

Порядок побочных эффектов сохраняется в отношении изменчивых операций чтения и записи ...

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


Ссылка в ответе на спецификацию C # 6, которая указана как Черновик. Спецификация C # 5 не является черновиком, но недоступна в режиме онлайн, только в виде загрузки . Одинаковая формулировка, насколько я вижу в этом разделе.

...