Как синхронизировать добавление из разных потоков, используя код без блокировки?
Я написал простой пример NasdaqIndex
расчет. Для простоты я объявил это как int
и это просто сумма цен 10
акций.
class NasdaqIndex {
private int[] stockValue = new int[10]; // for simplicity let's assume they are already initialized
// just sum from 1 to 10 of stockValue
private int nasdaqIndexValue; // for simplicity let's assume that already initialized
public void StockValueUpdated(int stockValueIndex, int newValue) {
int diff = newValue - stockValue[stockValueIndex];
stockValue[stockValueIndex] = newValue;
nasdaqIndexValue += diff; // THIS NEED TO BE SYNCHRONIZED!
}
}
Но в реальной жизни StockValueUpdated может (и будет) вызываться параллельно из разных потоков для разных stockValueIndex (он не будет вызываться параллельно для одного и того же stockValueIndex).
Поэтому мне нужно синхронизировать только одну строку кода nasdaqIndexValue += diff;
Например, если выполняется один поток nasdaqIndexValue += 10;
и другой поток выполнен nasdaqIndexValue += 3;
Я должен быть уверен, что в целом 13
добавлен. Нужна ли вообще синхронизация в этом случае? Если так, как это сделать, используя lock-free
код?
UPD oooops, я только что понял, что с помощью такого кода я добавляю маленькую "дельту" в nasdaqIndex каждый раз, когда я использую double. Поэтому я либо использую десятичную дробь, либо мне приходится иногда "полностью пересчитывать" nasdaqIndex, иначе через некоторое время она не будет равна сумме акций.
2 ответа
Использование Interlocked
введите, чтобы сделать эту операцию атомарной:
Interlocked.Add(ref nasdaqIndexValue, diff);
Использовать volatile
ключевое слово.
Модификатор volatile обычно используется для поля, к которому обращаются несколько потоков, без использования оператора блокировки для сериализации доступа.
private volatile int nasdaqIndexValue; // for simplicity let's assume that already initialized