блокировка объекта в асинхронной операции - PullRequest
3 голосов
/ 07 декабря 2010

У меня есть следующий код, с которым я хочу добиться следующего.

  1. Проверить, находится ли значение в кеше
  2. Если в кеше получить значение из него и продолжить
  3. Если его нет в кеше, выполнить логику для его вводав кеше, но сделать это асинхронно, так как операция может занять много времени, и я не хочу задерживать пользователя

Как вы увидите в моем коде, я устанавливаю блокировку накеш в асинхронном потоке.Безопасна ли моя установка под нитью?А установка блокировки будет означать, что кеш не будет доступен для других потоков для чтения из кеша, пока выполняется асинхронная операция.Я не хочу обстоятельство, когда кеш заблокирован в асинхронном потоке, препятствующем доступу к нему других запросов.

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

Любые рекомендации относительно того, как я мог бы улучшить код, были бы хорошими.

// Check if the value is in cache
        if (!this.Cache.Contains(key))
        {
            // Perform processing of files async in another thread so rendering is not slowed down
            ThreadPool.QueueUserWorkItem(delegate
            {
                lock (this.Cache)
                {
                    if (!this.Cache.Contains(key))
                    {
                        // Perform the operation to get value for cache here
                        var cacheValue = operation();

                        this.Cache.Add(key, cacheValue);
                    }
                }
            });

            return "local value";
        }
        else
        {
            // Return the string from cache as they are present there
            return this.Cache.GetFilename(key);
        }

Примечание: this.Cache представляет объект кэша.

Приложение представляет собой веб-приложение на .net 3.5.

Ответы [ 3 ]

1 голос
/ 07 декабря 2010

Как насчет изменения делегата, чтобы он выглядел так:

var cacheValue = operation();
lock (this.Cache)
            {
                if (!this.Cache.Contains(key))
                {
                    // Perform the operation to get value for cache here

                    this.Cache.Add(key, cacheValue);
                }
            }

Этот вид кодирования блокирует словарь на очень короткое время.Вы также можете попробовать использовать ConcurrentDictionary, который в большинстве случаев вообще не блокируется.

Alex.

1 голос
/ 07 декабря 2010

Есть несколько проблем с вашим кодом.Проблемы включают: вызов Cache.Contains за пределами lock, в то время как другие потоки могут изменять коллекцию;вызов operation в пределах lock, который может вызвать взаимоблокировки;и т.д.

Вот поточно-ориентированная реализация кэша, которая удовлетворяет всем вашим требованиям:

class Cache<TKey, TValue>
{
    private readonly ConcurrentDictionary<TKey, Task<TValue>> items;

    public Cache()
    {
        this.items = new ConcurrentDictionary<TKey, Task<TValue>>();
    }

    public Task<TValue> GetAsync(TKey key, Func<TKey, TValue> valueFactory)
    {
        return this.items.GetOrAdd(key,
            k => Task.Factory.StartNew<TValue>(() => valueFactory(k)));
    }
}

Метод GetAsync работает следующим образом: Сначала он проверяет, есть ли вitems словарь для данного ключа.Если такой Задачи нет, она запускает valueFactory в асинхронном режиме на ThreadPool и сохраняет объект Задачи, который представляет ожидающую асинхронную операцию в словаре.Код, вызывающий GetAsync, может дождаться завершения Задачи, что вернет значение, вычисленное как valueFactory.Все это происходит асинхронным, неблокирующим, поточно-ориентированным способом.

Пример использования:

var cache = new Cache<string, int>();

Task<int> task = cache.GetAsync("Hello World", s => s.Length);

// ... do something else ...

task.Wait();
Console.WriteLine(task.Result);
0 голосов
/ 07 декабря 2010

Выглядит как стандартное решение, кроме поиска в фоновом потоке.Это будет потокобезопасным, если все другие биты кода, использующие кеш, также снимают блокировку на той же ссылке кеша перед ее изменением.

Из вашего кода другие потоки все еще смогут читатьиз кэша (или пишите в него, если они не снимают блокировку (). Код будет блокироваться только в тот момент, когда встречается оператор lock ().

Делает ли возвращаемое "локальное значение"смысл? Вам не нужно было бы извлекать элемент в этой функции в любом случае в случае пропуска кэша?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...