Кэширование запросов для уменьшения обработки (TPL?) - PullRequest
1 голос
/ 21 марта 2012

В настоящее время я пытаюсь уменьшить количество аналогичных запросов, обрабатываемых на бизнес-уровне:

  1. Кэширование запросов, которые получает метод
  2. Выполнение задачи медленной обработки (один раз для всех аналогичных запросов)
  3. Возвращать результат для каждого вызова метода запроса

Обратите внимание на следующее:

  • Исходные вызовы метода не в настоящее время в асинхронном режиме BeginMethod () / EndMethod (IAsyncResult)
  • Запросы поступают быстрее, чем время, необходимое для генерации вывода
  • Я пытаюсь использоватьTPL, где это возможно, так как я сейчас пытаюсь узнать больше об этой библиотеке

например.Улучшение следующего

byte[] RequestSlowOperation(string operationParameter)
{
    Perform slow task here...
}

Есть мысли?

Продолжение:

    class SomeClass
{
    private int _threadCount;

    public SomeClass(int threadCount)
    {
        _threadCount = threadCount;
        int parameter = 0;

        var taskFactory = Task<int>.Factory;

        for (int i = 0; i < threadCount; i++)
        {
            int i1 = i;

            taskFactory
                .StartNew(() => RequestSlowOperation(parameter))
                .ContinueWith(result => Console.WriteLine("Result {0} : {1}", result.Result, i1));                                                  
        }            
    }

    private int RequestSlowOperation(int parameter)
    {
        Lazy<int> result2;
        var result = _cacheMap.GetOrAdd(parameter, new Lazy<int>(() => RequestSlowOperation2(parameter))).Value;            
        //_cacheMap.TryRemove(parameter, out result2); <<<<< Thought I could remove immediately, but this causes blobby behaviour

        return result;
    }

    static ConcurrentDictionary<int, Lazy<int>> _cacheMap = new ConcurrentDictionary<int, Lazy<int>>();
    private int RequestSlowOperation2(int parameter)
    {
        Console.WriteLine("Evaluating");
        Thread.Sleep(100);            
        return parameter;
    }
}

Ответы [ 2 ]

1 голос
/ 21 марта 2012

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

static var cacheMap = new ConcurrentDictionary<string, Lazy<byte[]>>();
byte[] RequestSlowOperation(string operationParameter)
{
    return cacheMap.GetOrAdd(operationParameter, () => new Lazy<byte[]>(() => RequestSlowOperation2(operationParameter))).Value;
}

byte[] RequestSlowOperation2(string operationParameter)
{
    Perform slow task here...
}

Это выполнит RequestSlowOperation2 не более одного раза для каждого ключа.Помните, что память, хранящаяся в словаре, никогда не будет освобождена.

Пользовательский делегат, переданный ConcurrentDictionary, не выполняется под блокировкой, что означает, что он может выполняться несколько раз!Мое решение позволяет создавать несколько лени, но только одна из них будет опубликована и материализована.

Относительно блокировки: это решение будет принимать блокировки, но это не имеет значения, потому что рабочие элементынамного дороже, чем (несколько) операций блокировки.

1 голос
/ 21 марта 2012

Честно говоря, использование TPL в качестве технологии здесь не очень важно, это просто проблема параллелизма. Вы пытаетесь защитить доступ к общему ресурсу (кешированным данным), и для этого единственный подход - это блокировка. Либо так, либо, если запись в кэше еще не существует, вы можете разрешить всем входящим потокам генерировать ее, а затем последующие инициаторы извлекают выгоду из кэшированного значения, как только оно будет сохранено, но в этом мало смысла, если генерировать ресурс медленно / дорого. и кеш.

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

...