Взаимно блокируемая видимость гарантии к другим потокам в C# или я должен все еще использовать энергозависимый?

Я читал ответ на подобный вопрос, но я все еще немного смущен... У Abel был большой ответ, но это - часть, в которой я не уверен:

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

Делает Interlocked видимость гарантии атомарной операции ко всем потокам, или делают я все еще должен использовать volatile ключевое слово на значении для гарантии видимости изменения?

Вот мой пример:

volatile int value = 100000; // <-- do I need the volitile keyword
// ....

public void AnotherThreadMethod()
{
 while(Interlocked.Decrement(ref value)>0)
 {
  // do something
 }
}


public void AThreadMethod()
{
 while(value > 0)
 {
  // do something
 }
}

Обновление:
Я был плохим спортом, и я изменил исходный пример, таким образом, здесь это снова:

public class CountDownLatch
{
    private volatile int m_remain; // <--- do I need the volatile keyword here?
    private EventWaitHandle m_event;

    public CountDownLatch(int count)
    {
        Reset(count);
    }

    public void Reset(int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}

6
задан Community 23 May 2017 в 11:55
поделиться

2 ответа

Их не * требуется ** волатильность, потому что вы никогда никогда не проверяете значение заблокированной переменной. Вместо этого вы всегда проверяете значение , возвращаемое заблокированной операцией (операциями). Смешивание взаимосвязанных операций и обычного присваивания / сравнения всегда приводит к неправильному коду.

Я не уверен, что такое функция Reset (), но этому фрагменту кода нет места в межпотоковом примитиве: вы присваиваете m_remain, вы напрямую проверяете значение m_remain, это очень плохо. Я настоятельно рекомендую вам убрать это: не только реализовано неправильно, но я очень сомневаюсь, что семантика «сброса» счетчика в середине срока службы необходима. Оставьте это просто: ctor (переместите в него код из Reset) Signal и Wait - единственные необходимые три оператора, и они верны, как и сейчас.

Обновлено После редактирования кода.

Игнорируя тот факт, что вы не должны смешивать эти два, если вы в конечном итоге смешаете их, то да, volatile все равно понадобится. Volatile в первую очередь касается кода IL и JIT-кода, сгенерированного, чтобы гарантировать, что значение всегда считывается из фактического места в памяти и не происходит оптимизации, такой как переупорядочение кода. Тот факт, что несвязанный фрагмент кода обновляет значение с помощью операций Interlocked, не влияет на другие части, считывающие значение.Без атрибута volatile компилятор / JIT может по-прежнему генерировать код, который игнорирует записи, которые происходят где-то в другом месте, не имеет значения, если записи являются блокированными или прямым назначением.

Кстати, существуют допустимые шаблоны, которые смешивают обычные операции чтения и заблокированные операции, но они обычно включают Interlocked.CompareExchange и делают следующее: чтение текущего состояния, выполнение некоторых вычислений на основе текущего состояния, попытка заменить состояние как заблокированное сравнение -exchange: в случае успеха нормально, в противном случае отбросить результат вычисления и вернуться к шагу 1.

5
ответ дан 17 December 2019 в 00:07
поделиться

Я думаю, что System.Threading.Thread.VolatileRead(ref myVariable) может быть тем, что вы ищете. Используя его в сочетании с Interlocked.Increment, можно гарантировать, что изменения будут атомарными, а значения, которые вы считываете, будут самыми последними.

2
ответ дан 17 December 2019 в 00:07
поделиться
Другие вопросы по тегам:

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