Зачем мне барьер памяти?

C # 4 в двух словах (настоятельно рекомендуется кстати) использует следующий код для демонстрации концепции MemoryBarrier (предполагая, что A и B выполнялись в разных потоках):

class Foo{
  int _answer;
  bool complete;
  void A(){
    _answer = 123;
    Thread.MemoryBarrier(); // Barrier 1
    _complete = true;
    Thread.MemoryBarrier(); // Barrier 2
  }
  void B(){
    Thread.MemoryBarrier(); // Barrier 3;
    if(_complete){
      Thread.MemoryBarrier(); // Barrier 4;
      Console.WriteLine(_answer);
    }
  }
}

они упоминают, что барьеры 1 и 4 предотвращают этот пример из записи 0 и барьеров 2 и 3 обеспечивает гарантию свежести : они гарантируют, что если B бежит за A, чтение _complete даст оценку true .

Я не совсем понял. Я думаю, я понимаю, почему барьеры 1 и 4 необходимы: мы не хотим, чтобы запись в _answer была оптимизирована и помещена после записи в _complete (барьер 1), и нам нужно чтобы убедиться, что _answer не кэшируется (Барьер 4). Я также думаю, что понимаю, почему необходим барьер 3: если A запускается только после записи _complete = true , B все равно потребуется обновить _complete , чтобы прочитать правильное значение.

Я не понимаю, зачем нам Барьер 2! Часть меня говорит, что это s потому что, возможно, поток 2 (запущенный B) уже выполнялся до (но не включая) if (_complete) , и поэтому нам нужно убедиться, что _complete обновлено.

Однако я не понимаю, как это помогает. Возможно ли, что для _complete будет установлено значение true в A, но метод B увидит кэшированную (ложную) версию _complete ? То есть, если поток 2 запускал метод B до тех пор, пока после первого MemoryBarrier, а затем поток 1 не выполнял метод A до _complete = true , но не дальше, а затем поток 1 возобновил и протестировал if (_complete) - может ли это , если не приведет к ложному ?

34
задан starblue 9 October 2012 в 09:01
поделиться

1 ответ

Барьер №2 гарантирует немедленную фиксацию записи в _complete . В противном случае он может оставаться в состоянии очереди, что означает, что чтение _complete в B не увидит изменения, вызванного A , даже если B ] эффективно использовал изменчивое чтение.

Конечно, этот пример не совсем отражает проблему, потому что A больше ничего не делает после записи в _complete , что означает, что запись в любом случае будет завершена немедленно, поскольку поток заканчивается рано.

Ответ на ваш вопрос о том, может ли if по-прежнему оцениваться как false , да по точно указанным вами причинам. Но обратите внимание на то, что автор говорит по этому поводу.

Этому примеру препятствуют барьеры 1 и 4. от написания «0».Барьеры 2 и 3 дают гарантию свежести: они убедитесь, что , если B идет после A , чтение _complete будет иметь значение true.

Акцент на том, что «если B побежит после A», принадлежит мне. Конечно, может случиться так, что два потока чередуются. Но автор проигнорировал этот сценарий, по-видимому, чтобы указать на то, как Thread.MemoryBarrier работает проще.

Между прочим, мне было нелегко придумать пример на моей машине, где барьеры №1 и №2 изменили бы поведение программы. Это потому, что в моей среде была сильна модель памяти относительно записи. Возможно, если бы у меня была многопроцессорная машина, я бы использовал Mono или какую-то другую настройку, я мог бы это продемонстрировать. Конечно, было легко продемонстрировать, что снятие барьеров №3 и №4 оказало влияние.

27
ответ дан 27 November 2019 в 17:13
поделиться
Другие вопросы по тегам:

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