Какие варианты у меня есть, чтобы сделать этот поток кода безопасным? - PullRequest
1 голос
/ 19 июня 2010

У меня есть этот фрагмент кода, много вещей пропущено для краткости, но сцена такова:

 public class Billing
    {
        private List<PrecalculateValue> Values = new List<PrecalculateValue>();

        public int GetValue(DateTime date)
        {
            var preCalculated = Values.SingleOrDefault(g => g.date == date).value;
            //if exist in Values, return it
            if(preCalculated != null)
            {
               return preCalculated;
            }

            // if it does not exist calculate it and store it in Values
            int value = GetValueFor(date);
            Values.Add(new PrecalculateValue{date = date, value = value});

            return value;
        }

        private object GetValueFor(DateTime date)
        {
            //some logic here
        }
    }

У меня есть List<PrecalculateValue> Values, где я храню все значения, которые я уже рассчитал для позжеЯ делаю это главным образом потому, что не хочу дважды пересчитывать данные для одного и того же клиента, каждый расчет включает в себя много операций и занимает от 500 до 1000 мс, и есть большая вероятность повторного использования этого значения из-за некоторыхрекурсия, включенная в класс выставления счетов за дырырезультат в коллекции.Поэтому я проверил список, и он сохранил значения обоих клиентов в одном списке.Что я могу сделать, чтобы избежать этой маленькой проблемы?

Ответы [ 2 ]

5 голосов
/ 19 июня 2010

Ну, во-первых, эта строка:

return Values.Single(g => g.date == date).value;

делает так, чтобы последующие строки никогда не вызывались.Я предполагаю, что вы немного перефразировали свой код здесь?

Если вы хотите синхронизировать записи в ваш список Values, самый простой способ будет lock для общего объекта повсюду вкод, который вы изменяете в списке:

int value = GetValueFor(date);

lock (dedicatedLockObject) {
    Values.Add(new PrecalculateValue{date = date, value = value});
}

return value;

Но здесь стоит отметить еще кое-что: поскольку похоже, что вы хотите иметь один PrecalculateValue на DateTime, более подходящая структура данных, вероятно, будетбыть Dictionary<DateTime, PrecalculateValue> - он обеспечит молниеносный поиск O (1) на основе вашей клавиши DateTime, по сравнению с List<PrecalculateValue>, который должен был бы повторяться, чтобы найти то, что вы ищете.

С этим изменением ваш код может выглядеть примерно так:

public class Billing
{
    private Dictionary<DateTime, PrecalculateValue> Values = 
        new Dictionary<DateTime, PrecalculateValue>();

    private readonly commonLockObject = new object();

    public int GetValue(DateTime date)
    {
        PrecalculateValue cachedCalculation;

        // Note: for true thread safety, you need to lock reads as well as
        // writes, to ensure that a write happening concurrently with a read
        // does not corrupt state.
        lock (commonLockObject) {
            if (Values.TryGetValue(date, out cachedCalculation))
                return cachedCalculation.value;
        }

        int value = GetValueFor(date);

        // Here we need to check if the key exists again, just in case another
        // thread added an item since we last checked.
        // Also be sure to lock ANYWHERE ELSE you're manipulating
        // or reading from the collection.
        lock (commonLockObject) {
            if (!Values.ContainsKey(date))
                Values[date] = new PrecalculateValue{date = date, value = value};
        }

        return value;
    }

    private object GetValueFor(DateTime date)
    {
        //some logic here
    }
}

И последний совет: если не важно, чтобы в вашей коллекции существовало не более одного значения.метод Single является излишним.Если вы предпочитаете просто получить первое значение и не обращать внимания на потенциальные дубликаты, First является одновременно более безопасным (как, с меньшей вероятностью исключения) и более быстрым (потому что он не должен повторяться по всей коллекции).

1 голос
/ 19 июня 2010

Можно использовать что-то вроде этого

public int GetValue(DateTime date)
{

    var result = Values.Single(g => g.date == date) ?? GetValueFor(date);

    lock (Values)
    {
        if (!Values.Contains(result)) Values.Add(result);
    }
    return result.value;
}

private PrecalculateValue GetValueFor(DateTime date)
{
    //logic
    return new PrecalculateValue() ;
}

Советую использовать словарь для списка пар ключ-значение.

...