我目前正在尝试通过以下方式减少在业务层中处理的类似请求的数量:
需要注意的是:
例如。改善以下
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;
}
}
答案 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,但其中只有一个将被发布和实现。
关于锁定:此解决方案将获取锁定,但这并不重要,因为工作项远比(少数)锁定操作昂贵。