C# 缓存IEnumerable的性能<;T>;实施
[编辑] 新方法使用扩展方法C# 缓存IEnumerable的性能<;T>;实施,c#,performance,multithreading,caching,ienumerable,C#,Performance,Multithreading,Caching,Ienumerable,[编辑] 新方法使用扩展方法System.Linq.EnumerableEx.MemoizeAll()解决了下面概述的问题 在内部,MemoizeAll()使用一个System.Linq.EnumerableEx.memoizealnumerable(可在System.Interactive程序集中找到),它类似于我的ThreadSafeCachedEnumerable(sorta) 下面是一个精心设计的示例,它非常缓慢地打印可枚举(数字1-10)的内容,然后第二次快速打印内容(因为它缓存了值)
System.Linq.EnumerableEx.MemoizeAll()
解决了下面概述的问题
在内部,MemoizeAll()
使用一个System.Linq.EnumerableEx.memoizealnumerable
(可在System.Interactive程序集中找到),它类似于我的ThreadSafeCachedEnumerable
(sorta)
下面是一个精心设计的示例,它非常缓慢地打印可枚举(数字1-10)的内容,然后第二次快速打印内容(因为它缓存了值):
//使用Thread.Sleep()模拟工作,创建一个包含数字1-10的可枚举项
var slowEnum=EnumerableEx.Generate(1,currentNum=>(currentNum currentNum,previousNum=>{Thread.Sleep(250);返回previousNum+1;});
//这将使用一个缓存每个值的枚举来装饰慢速枚举。
var cachedEnum=slowEnum.MemoizeAll();
//打印数字
foreach(cachedEnum中的var num.Repeat(2))
{
控制台写入线(num);
}
[/EDIT]
你好,多线程大师
我创建了ThreadSafeCachedEnumerable类,目的是在重用长时间运行的查询时提高性能。其想法是从IEnumerable中获取一个枚举数,并在每次调用MoveNext()时向缓存中添加项。以下是我当前的实现:
/// <summary>
/// Wraps an IEnumerable<T> and provides a thread-safe means of caching the values."/>
/// </summary>
/// <typeparam name="T"></typeparam>
class ThreadSafeCachedEnumerable<T> : IEnumerable<T>
{
// An enumerator from the original IEnumerable<T>
private IEnumerator<T> enumerator;
// The items we have already cached (from this.enumerator)
private IList<T> cachedItems = new List<T>();
public ThreadSafeCachedEnumerable(IEnumerable<T> enumerable)
{
this.enumerator = enumerable.GetEnumerator();
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
// The index into the sequence
int currentIndex = 0;
// We will break with yield break
while (true)
{
// The currentIndex will never be decremented,
// so we can check without locking first
if (currentIndex < this.cachedItems.Count)
{
var current = this.cachedItems[currentIndex];
currentIndex += 1;
yield return current;
}
else
{
// If !(currentIndex < this.cachedItems.Count),
// we need to synchronize access to this.enumerator
lock (enumerator)
{
// See if we have more cached items ...
if (currentIndex < this.cachedItems.Count)
{
var current = this.cachedItems[currentIndex];
currentIndex += 1;
yield return current;
}
else
{
// ... otherwise, we'll need to get the next item from this.enumerator.MoveNext()
if (this.enumerator.MoveNext())
{
// capture the current item and cache it, then increment the currentIndex
var current = this.enumerator.Current;
this.cachedItems.Add(current);
currentIndex += 1;
yield return current;
}
else
{
// We reached the end of the enumerator - we're done
yield break;
}
}
}
}
}
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
//
///包装IEnumerableT并提供线程安全的方法来缓存值。“/>
///
///
类ThreadSafeCachedEnumerable:IEnumerable
{
//来自原始IEnumerable的枚举数
私有IEnumerator枚举器;
//我们已经缓存的项(来自此.enumerator)
private IList cachedItems=新列表();
public ThreadSafeCachedEnumerable(IEnumerable enumerable)
{
this.enumerator=enumerable.GetEnumerator();
}
#区域可数成员
公共IEnumerator GetEnumerator()
{
//将索引放入序列中
int currentIndex=0;
//我们将以产量突破来打破僵局
while(true)
{
//currentIndex永远不会递减,
//所以我们可以先检查而不锁定
if(currentIndex
当缓存中没有更多的项时,我只是简单地“锁定(this.enumerator)”,以防另一个线程正要添加另一项(我认为从两个线程调用this.enumerator上的MoveNext()是个坏主意) 在检索以前缓存的项目时,性能非常好,但在第一次获取多个项目时(由于不断锁定),性能开始下降。有没有提高性能的建议
谢谢!一些建议:
字典
或哈希集
。同样,在调用之间可能会删除项目,从而使缓存无效在.NET中锁定通常非常快(如果没有争用)。分析是否将锁定确定为性能问题的根源?在基础枚举器上调用
MoveNext
需要多长时间
此外,当前的代码不是线程安全的。您不能在一个线程上安全地调用this.cachedItems[currentIndex]
(在if(currentIndex)中
/// <summary>
/// Wraps an IEnumerable<T> and provides a thread-safe means of caching the values."/>
/// </summary>
/// <typeparam name="T"></typeparam>
class ThreadSafeCachedEnumerable<T> : IEnumerable<T>
{
// An enumerator from the original IEnumerable<T>
private IEnumerator<T> enumerator;
// The items we have already cached (from this.enumerator)
private IList<T> cachedItems = new List<T>();
public ThreadSafeCachedEnumerable(IEnumerable<T> enumerable)
{
this.enumerator = enumerable.GetEnumerator();
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
// The index into the sequence
int currentIndex = 0;
// We will break with yield break
while (true)
{
// The currentIndex will never be decremented,
// so we can check without locking first
if (currentIndex < this.cachedItems.Count)
{
var current = this.cachedItems[currentIndex];
currentIndex += 1;
yield return current;
}
else
{
// If !(currentIndex < this.cachedItems.Count),
// we need to synchronize access to this.enumerator
lock (enumerator)
{
// See if we have more cached items ...
if (currentIndex < this.cachedItems.Count)
{
var current = this.cachedItems[currentIndex];
currentIndex += 1;
yield return current;
}
else
{
// ... otherwise, we'll need to get the next item from this.enumerator.MoveNext()
if (this.enumerator.MoveNext())
{
// capture the current item and cache it, then increment the currentIndex
var current = this.enumerator.Current;
this.cachedItems.Add(current);
currentIndex += 1;
yield return current;
}
else
{
// We reached the end of the enumerator - we're done
yield break;
}
}
}
}
}
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}