2015-09-28 2 views
6

Просматривая исходный код MEF, я нашел этот кусок. Может кто-нибудь объяснить, почему MemoryBarrier нужен внутри замка?Почему здесь используется Thread.MemoryBarrier?

Весь метод:

public void SatisfyImportsOnce(ComposablePart part) 
{ 
    this.ThrowIfDisposed(); 

    if (this._importEngine == null) 
    { 
     ImportEngine importEngine = new ImportEngine(this, this._compositionOptions); 

     lock(this._lock) 
     { 
      if (this._importEngine == null) 
      { 
       Thread.MemoryBarrier(); 
       this._importEngine = importEngine; 
       importEngine = null; 
      } 
     } 
     if(importEngine != null) 
     { 
      importEngine.Dispose(); 
     } 
    } 
    this._importEngine.SatisfyImportsOnce(part); 
} 
+0

Это * кажется *, что иногда, замок не хватает –

+0

Это невозможно ответить на этот вопрос, не зная намного больше контекста. –

+3

Это FUD на процессоре со слабой моделью памяти, некоторые программисты Microsoft, вероятно, никогда не оправятся от необходимости приручать Itanium. Это гарантирует, что другой поток может наблюдать полностью построенный объект, когда он использует ссылку _importEngine. На слабом процессоре эта ссылка может быть записана в память до того, как будут записаны поля объекта, чтобы в другом потоке можно было увидеть значение неинициализированного поля. Не обязательно, так как .NET 2.0 и определенно не нужен здесь, поскольку блокировка уже подразумевает барьер памяти. –

ответ

1

Thread.MemoryBarrier предотвращает джиттера/компилятор любые инструкции переназначения для оптимизации кода.

В Treading in C#, by Joe Albahari книге он саи:

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

В этом примере может быть указано, что значения importEngine или _importEngine кэшируются, и очень важно, чтобы все потоки были немедленно уведомлены об изменениях.

Также MemoryBarrier в этом случае обеспечивает importEngine свежесть гарантии, прежде чем он назначен _importEngine

+1

Но замок также подразумевает барьер памяти. Ни 'importEngine', ни' _importEngine' не могут быть кэшированы внутри блокировки. (если другой материал не записывается в '_importEngine' без блокировки на' this._lock'). –

+0

@ScottChamberlain Вы правы. И поэтому я думаю, что в этом случае используется MemoryBarrier, могут быть другие потоки, которые записываются в _importEnginge без использования _lock. –

+2

В CLI (стандартизованная версия CLR) базовые нулевые проверки двунаправленной блокировки идиомы являются недостаточными. Модель памяти CLI потребует, чтобы переменная была изменчивой или использовала явный барьер. CLR имеет более сильную модель памяти, и не требуется ни летучих, ни барьеров. Авторы этого кода, возможно, не знали о дополнительной гарантии CLR, или, как Джон Скит предпочитает не полагаться на них. Барьер имеет ограниченную стоимость, поскольку после инициализации и синхронизации, более поздние вызовы будут полностью пропускать содержимое блокировки. –

Смежные вопросы