在异步操作中锁定对象

时间:2010-12-06 23:47:23

标签: c# multithreading caching asynchronous

我有以下代码,我想实现以下代码。

  1. 检查某个值是否在缓存中
  2. 如果在缓存中,从中获取值并继续
  3. 如果不在缓存中,请执行逻辑以在缓存中输入它,但执行此异步操作,因为执行此操作可能需要很长时间而且我不想阻止用户
  4. 正如您将在我的代码中看到的那样,我在异步线程中对缓存进行了锁定。我的设置是否安全?并且通过放置锁定意味着当异步操作发生时,其他线程无法从高速缓存中读取高速缓存。我不希望在异步线程中锁定缓存的情况阻止其他请求访问它。

    也有可能由多个线程调用相同的请求,因此锁定。

    任何关于如何改进代码的建议都会很棒。

    // 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上的Web应用程序。

3 个答案:

答案 0 :(得分:1)

如何将委托更改为如下所示:

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,它几乎不会发生任何锁定。

亚历。

答案 1 :(得分:1)

您的代码存在一些问题。问题包括:在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字典中是否有任务。如果没有这样的Task,它会在valueFactory上异步运行ThreadPool,并在代码中存储表示挂起的异步操作的Task对象。代码调用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);

答案 2 :(得分:0)

看起来像标准解决方案,但后台线程中的检索除外。它是线程安全的,只要使用缓存的代码的所有其他位也在修改它之前取消对同一缓存引用的锁定。

从你的代码中,其他线程仍然可以从缓存中读取(或者如果它们没有取出锁()则写入它。代码只会在遇到lock()语句时阻塞

回报“本地价值”是否有意义?在缓存未命中的情况下,您是否还不需要检索该函数中的项目?