缓存请求以减少处理(TPL?)

时间:2012-03-20 21:29:32

标签: c#-4.0 task-parallel-library

我目前正在尝试通过以下方式减少在业务层中处理的类似请求的数量:

  1. 缓存方法接收的请求
  2. 执行慢速处理任务(一次针对所有类似请求)
  3. 将结果返回给每个请求方法调用
  4. 需要注意的是:

    • 原始方法调用当前处于异步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 个答案:

答案 0 :(得分:1)

老实说,在这里使用TPL作为一种技术并不重要,这只是一个直接的并发问题。您正在尝试保护对共享资源(缓存数据)的访问,为此,唯一的方法是 来锁定。或者,如果缓存条目尚不存在,您可以允许所有传入线程生成它,然后后续请求者一旦存储就从缓存值中受益,但如果资源生成缓慢/昂贵,则没有什么价值和缓存。

也许更多细节会清楚说明你为什么要在没有锁定的情况下完成这项工作。如果有更多细节可以让你更清楚你想要做什么,我会很高兴地修改我的答案。

答案 1 :(得分:1)

这是一种快速,安全且可维护的方法:

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的用户委托不会在锁定下执行,这意味着它可以执行多次!我的解决方案允许创建多个lazies,但其中只有一个将被发布和实现。

关于锁定:此解决方案获取锁定,但这并不重要,因为工作项远比(少数)锁定操作昂贵。