Как синхронизировать добавление из разных потоков, используя код без блокировки?

Я написал простой пример 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
Другие вопросы по тегам