Безопасно ли смешивать блокировки и операции блокировки? - PullRequest
6 голосов
/ 08 декабря 2010

У меня есть некоторый код, который должен быть безопасным для потоков, поведение которого напоминает это:


protected long m_RunningValue
protected long m_RunningCounter
protected object m_Lock = new object();

public long RunningValue { get { return Interlocked.Read(m_RunningValue); } }
public long RunningCounter { get { return Interlocked.Read(m_RunningCounter); } }

public void DoCalculation(int newValue, int newQuantity)
{
   lock(m_Lock)
   {
       Interlocked.Add(ref m_RunningValueA, newValue);
       Interlocked.Add(ref m_RunningCounter, newQuantity);
       if(Interlocked.Read(ref newQuantity) == 0)
       { 
         ...m_RunningValue gets further modified here
       }
   }
}

Вычисление должно блокировать как значение, так и счетчик, иначе условие гонки может повлиять на блок if (...), однако их вообще не нужно синхронизировать при считывании, т.е. если счетчик и значение изменяются между попытками прочитать оба, это на 100% нормально для меня.

Имеется блокировка чтения для чтения 64-битного значения для обеспечения безопасности потока.

  1. Безопасно ли смешивать блокировки и блокировки? Я читал на других веб-страницах, что смешивать их небезопасно, однако я не могу найти разъяснения, если это означает, что смешивание их - отличный способ внести незначительные ошибки или если на системном уровне это может повредить соответствующие структуры данных. 1010 *

  2. Является ли стоимость всей этой блокировки (64-разрядной среды выполнения .NET 4.0) полностью разрушающей цель сохранения блокировки ReaderWriterSlim вокруг свойства get () метода?

1 Ответ

5 голосов
/ 08 декабря 2010

Что касается отредактированной версии, это , а не , гарантированно поточно-ориентированный.

Рассмотрим 32-битную платформу. Это означает, что 64-битные длинные значения должны быть доступны в двух отдельных операциях.

Вполне возможно, что один поток прочитает первую половину числа, затем поменяется местами с ЦП, а вторая половина значения изменится за его спиной. Теперь этот поток имеет совершенно недопустимое значение.

Документы для Interlocked.Read прямо указывают, что:

Метод Read и 64-разрядные перегрузки методов Increment, Decrement и Add действительно атомарны только в системах, где System.IntPtr имеет длину 64 бита. В других системах эти методы являются атомарными по отношению друг к другу, но не по отношению к другим средствам доступа к данным. Таким образом, для обеспечения безопасности потоков в 32-разрядных системах любой доступ к 64-разрядному значению должен осуществляться через члены класса Interlocked.

(акцент мой)

РЕДАКТИРОВАТЬ: Теперь, когда это было отредактировано назад, это выглядит немного глупо, но я оставлю это здесь, чтобы подчеркнуть, что если вы блокируете в некоторых местах, но не в других, это вроде бессмысленно.

...