Действительно Контролирует. Ожидайте гарантируют, что поля перечитаны?

Это является общепринятым (я верю!) это a lock вынудит любые значения поля быть перезагруженными (по существу действующий как барьер памяти или забор - моя терминология в этой области становится немного свободной, я боюсь), с последствием, что поля, к которым только когда-либо получают доступ внутри a lock не должны самостоятельно быть volatile.

(Если я уже неправ, просто говорю!)

Хороший комментарий был повышен здесь, подвергнув сомнению, верно ли то же, если код делает a Wait() - т.е. после того как это было Pulse()d, будет он перезагружать поля из памяти, или могли они быть в регистре (и т.д.).

Или проще: делает поле, должен быть volatile гарантировать, что текущее значение получено при возобновлении после a Wait()?

Рассмотрение отражателя, Wait раскритиковывает в ObjWait, который является managed internalcall (то же как Enter).

Рассматриваемый сценарий был:

bool closing;
public bool TryDequeue(out T value) {
    lock (queue) { // arbitrary lock-object (a private readonly ref-type)
        while (queue.Count == 0) {
            if (closing) {       // <==== (2) access field here
                value = default(T);
                return false;
            }
            Monitor.Wait(queue); // <==== (1) waits here
        }
        ...blah do something with the head of the queue
    }
}

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

22
задан Community 23 May 2017 в 12:09
поделиться

3 ответа

Поскольку метод Wait () освобождает и повторно устанавливает блокировку Monitor , если lock выполняет семантику забора памяти, затем Monitor.Wait () также будет.

Надеюсь ответить на ваш комментарий:

Поведение при блокировке Monitor.Wait () находится в документации ( http://msdn.microsoft.com/en-us/library /aa332339.aspx), курсив добавлен:

Когда поток вызывает Wait, он снимает блокировку с объекта и входит в очередь ожидания объекта. Следующий поток в очереди готовности объекта (если таковая имеется) получает блокировку и эксклюзивно использует объект. Все потоки, вызывающие Wait , остаются в очереди ожидания до тех пор, пока не получат сигнал от Pulse или PulseAll , отправленный владельцем блокировки. Если отправляется Импульс , затрагивается только поток в начале очереди ожидания. Если отправлено PulseAll , затронуты все потоки, ожидающие объекта. Когда сигнал получен, один или несколько потоков покидают очередь ожидания и входят в очередь готовности.Потоку в очереди готовности разрешено повторно получить блокировку.

Этот метод возвращается, когда вызывающий поток повторно захватывает блокировку объекта .

Если вы спрашиваете о ссылке на то, подразумевает ли блокировка / полученный монитор барьер памяти, в спецификации ECMA CLI говорится следующее:

12.6.5 Блокировки и потоки:

Получение блокировки ( System.Threading.Monitor.Enter или ввод синхронизированного метода) должно неявно выполнять операцию непостоянного чтения и снимать блокировку ( ] System.Threading.Monitor.Exit или выход из синхронизированного метода) должен неявно выполнять операцию энергозависимой записи. См. §12.6.7.

12.6.7 Энергозависимое чтение и запись:

Энергозависимое чтение имеет «семантику получения», означающую, что чтение гарантированно произойдет до любых обращений к памяти, которые происходят после инструкции чтения в последовательности инструкций CIL. Энергозависимая запись имеет "семантику освобождения", означающую, что запись гарантированно произойдет после любых обращений к памяти до инструкции записи в последовательности инструкций CIL.

Кроме того, в этих записях блога есть некоторые подробности, которые могут быть интересны:

18
ответ дан 29 November 2019 в 05:35
поделиться

В дополнение к ответу Майкла Берра, Ожидание не только освобождает и повторно устанавливает блокировку, но и делает это так, чтобы другой поток может снять блокировку, чтобы проверить общее состояние и вызвать Pulse . Если второй поток не снимает блокировку, то Pulse выбросит. Если они этого не сделают, Pulse Wait первого потока не вернется.Следовательно, доступ любого другого потока к общему состоянию должен происходить в рамках правильного сценария без памяти.

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

4
ответ дан 29 November 2019 в 05:35
поделиться

Возможно, я смогу помочь вам на этот раз... вместо volatile вы можете использовать Interlocked.Exchange с целым числом.

if (closing==1) {       // <==== (2) access field here
    value = default(T);
    return false;
}

// somewhere else in your code:
Interlocked.Exchange(ref closing, 1);

Interlocked.Exchange - это механизм синхронизации, volatile - нет... Надеюсь, это чего-то стоит (но вы, вероятно, уже думали об этом).

1
ответ дан 29 November 2019 в 05:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: