Использование барьеров памяти

В следующем примере кода требуется ли барьер памяти в FuncA для обеспечения чтения самого последнего значения?

class Foo
{
   DateTime m_bar;

   void FuncA() // invoked by thread X
   {
      Thread.MemoryBarrier(); // is required?
      Console.WriteLine(m_bar);
   }

   void FuncB() // invoked by thread Y
   {
       m_bar = DateTime.Now;
   }       
}

EDIT: если нет, как я могу гарантировать, что FuncA прочитает самое последнее значение? (Я хочу убедиться, что последнее значение действительно хранится в кеше процессора) [без использования блокировок]


person Community    schedule 13.11.2009    source источник
comment
А еще... звучит как домашняя работа ›_›   -  person JustLoren    schedule 13.11.2009
comment
на самом деле нет, просто ради интереса..   -  person    schedule 13.11.2009
comment
Зачем ты убрал тело? Если вам больше не нравится ваш вопрос, удалите его, не считайте его бесполезным пустым вопросом.   -  person Jorge Córdoba    schedule 13.11.2009
comment
не могу удалить его, так как слишком много ответов / голосов.   -  person    schedule 13.11.2009
comment
Не закрывайте его, это слишком грязно, чтобы удалить содержимое тела и оставить пустой вопрос. Вопрос несколько актуальный и интересный, просто оставьте как есть. Никто не голосует против, и я действительно нахожу это интересным вопросом.   -  person Jorge Córdoba    schedule 13.11.2009


Ответы (5)


Для меня это звучит как большое «нет». Thread.MemoryBarrier() синхронизирует доступ к памяти только в том потоке, который его реализовал.

Из MSDN:

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

person JustLoren    schedule 13.11.2009

Я предлагаю вам хранить дату и время как количество тиков (это тип «длинный», т.е. Int64), вы можете легко преобразовать тики (новый DateTime (тики)) и тики (myDateTime.Ticks). Затем вы можете использовать Interlocked.Read для чтения значения и Interlocked.Exchange для записи значения в быстрых неблокирующих операциях.

person Dmitry Osinovskiy    schedule 13.11.2009
comment
Это единственный способ сделать это без использования блоков синхронизации (Monitor.Enter и Exit, он же lock(foo)), поскольку Interlocked работает только с long или int. - person Yann Schwartz; 13.11.2009
comment
Пример здесь: stackoverflow.com/questions/ 1531668/ - person Yann Schwartz; 13.11.2009

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

Если барьера памяти нет, то поток X может прочитать значение m_bar из своей собственной строки кэша, пока это значение не было записано обратно в основную память (изменение было сделано локально для потока Y). Вы можете добиться того же эффекта, объявив переменную как volatile:

Модификатор volatile обычно используется для поля, доступ к которому осуществляется несколькими потоками без использования оператора блокировки для сериализации доступа. Использование модификатора volatile гарантирует, что один поток получит самое актуальное значение, записанное другим потоком.

Хорошая статья на эту тему (вероятно, лучшая) принадлежит Джо Даффи: Нестабильные операции чтения и записи и своевременность

person Jorge Córdoba    schedule 13.11.2009
comment
Интересно, что описание MemoryBarrier различается в .NET 2.0 и .NET 3.5 2.0 => Синхронизирует память. По сути, сбрасывает содержимое кэш-памяти в основную память для процессора, выполняющего текущий поток. 3.5 ==> Синхронизирует доступ к памяти следующим образом: Процессор, выполняющий текущий поток, не может переупорядочивать инструкции таким образом, чтобы обращения к памяти до вызова MemoryBarrier выполнялись после обращений к памяти, следующих за вызовом MemoryBarrier. - person Jorge Córdoba; 13.11.2009

Барьер памяти действует так же, как и блокировка, гарантирует, что поле получит свое последнее значение из памяти при входе в блокировку и будет записано в память перед выходом из блокировки.
Убедитесь, что значение поля всегда читается или записывается в память и никогда не оптимизируется путем чтения или записи сначала в кеш процессора, также может быть достигнуто с помощью ключевого слова volatile.
Если только примитивные целочисленные типы и ссылочные типы DateTime не могут быть кэшированы в регистрах ЦП, поэтому нет необходимости (и не может) быть объявлено с ключевым словом volatile.

person Mez    schedule 13.11.2009

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

person Community    schedule 14.11.2009