Летучий и нитевидный.Перед вызовом memorybarrier в C#
для реализации блокировка бесплатный код для многопоточного приложения я использовал volatile
переменные,
теоретически: элемент volatile
ключевое слово просто используется, чтобы убедиться, что все потоки видят самое обновленное значение изменчивой переменной; поэтому, если поток A
обновляет значение переменной и нити B
прочитайте эту переменную сразу после того, как это обновление произойдет, он увидит самое обновленное значение, написанное недавно из потока A.
Как я прочитал в в C# 4.0 в в двух словах!--17--> книга
это неправильно, потому что
применение volatile не предотвращает замену записи с последующим чтением.
может ли эта проблема быть решена путем сдачи Thread.MemoryBarrier()
перед каждым на volatile
переменной типа:
private volatile bool _foo = false;
private void A()
{
//…
Thread.MemoryBarrier();
if (_foo)
{
//do somthing
}
}
private void B()
{
//…
_foo = true;
//…
}
и если это решает проблему; рассмотрим, что у нас есть цикл while, который зависит от этого значения при одном из его условий; ставит Thread.MemoryBarrier()
перед while loop-это правильный способ исправить проблему? пример:
private void A()
{
Thread.MemoryBarrier();
while (_someOtherConditions && _foo)
{
// do somthing.
}
}
чтобы быть более точным, я хочу, чтобы _foo
переменная, чтобы дать свое самое свежее значение, когда любой поток просит его в любое время; поэтому, если вставить Thread.MemoryBarrier()
перед вызовом переменной устраняет проблему, тогда я мог бы использовать Foo
собственность вместо _foo
и Thread.MemoryBarrier()
в пределах получения этого свойства, как:
Foo
{
get
{
Thread.MemoryBarrier();
return _foo;
}
set
{
_foo = value;
}
}
5 ответов
"C# в двух словах" является правильным, но его утверждение спорно. Почему?
- "write", за которым следует "read", без "volatile", гарантированно происходит в программном порядке в любом случае если это влияет на логику в пределах одного потока
- "write" перед "read" в многопоточной программе -совершенно бессмысленно беспокоиться о в вашем примере.
давайте уточним. Возьмите оригинал код:
private void A()
{
//…
if (_foo)
{
//do something
}
}
что произойдет, если планировщик потоков уже проверил _foo
переменная, но она приостанавливается непосредственно перед //do something
комментарии? Ну, в этот момент другой поток может изменить значение _foo
, что означает, что все ваши летучие вещества и нити.MemoryBarriers рассчитывали зря!!! Если это абсолютно необходимо, чтобы do_something
следует избегать, если значение _foo
false, тогда у вас нет выбора, кроме как использовать замок.
однако, если это это нормально для do something
для выполнения, когда внезапно _foo
становится false, то это означает, что ключевое слово volatile было более чем достаточно для ваших нужд.
чтобы быть ясным: все ответчики, которые говорят вам использовать барьер памяти, неверны или предоставляют излишек.
книги правильно.
Модель памяти среды CLR указывает, что операции загрузки и хранения могут быть переупорядочены. Это касается volatile и энергонезависимые переменные.
объявление переменной как volatile
означает только, что операции загрузки будут иметь приобрести семантика, и операции хранения будут иметь релиз семантика. Кроме того, компилятор будет избегать выполнения определенных оптимизаций, которые ретранслируют тот факт, что переменная доступна в сериализованном, однопоточном способе (например, поднимая нагрузку/магазины из петель).
С помощью volatile
ключевое слово само по себе не создает критических разделов, и это не заставляет потоки волшебным образом синхронизироваться друг с другом.
вы должны быть очень осторожно, когда вы пишете код блокировки. В этом нет ничего простого, и даже у экспертов есть проблемы, чтобы сделать это правильно.
Все проблемы вы пытаетесь решите, вероятно, есть гораздо более разумный способ сделать это.
во втором примере Вам также нужно будет поставить Thread.MemoryBarrier();
внутри цикла, чтобы убедиться, что вы получаете самое последнее значение каждый раз, когда вы проверяете условие цикла.
вытащил из здесь...
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.
Итак, если мы вернемся к вашему примеру цикла...вот как это должно выглядеть...
private void A()
{
Thread.MemoryBarrier();
while (_someOtherConditions && _foo)
{
//do somthing
Thread.MemoryBarrier();
}
}
собственные слова Microsoft о барьерах памяти:
MemoryBarrier требуется только в многопроцессорных системах со слабым порядком памяти (например, в системе, использующей несколько процессоров Intel Itanium).
для большинства целей оператор блокировки C#, оператор SyncLock Visual Basic или класс Monitor предоставляют более простые способы синхронизации данных.