Kes*_*ess 10 c# multithreading concurrentdictionary
当我调用ConcurrentDictionary.ToArray时,有时我会收到以下错误.错误如下:
System.ArgumentException:索引等于或大于数组的长度,或者字典中的元素数大于从索引到目标数组末尾的可用空间.at System.Collections.Concurrent.ConcurrentDictionary
2.System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair
2 [] array,Int32 index)at System.Linq.Buffer1..ctor(IEnumerable
1 source)at System.Linq.Enumerable.ToArray [TSource](IEnumerable1 source) at ...Cache.SlidingCache
2.RemoveExcessAsync(Object state)in ...\SlidingCache.cs:System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean preserveSyncCtx)的第141行,位于System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean preserveSyncCtx) System.Threading.ThreadPoolWorkQueue.Dispatch()中的.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
我注意到在多线程场景中,有时在对ConcurrentDictionary进行排序时会出现异常.见堆栈溢出的问题在这里.所以我在排序之前开始使用ConcurrentDictionary.ToArray.在创建阵列时似乎仍然存在问题.
并发字典用于缓存,当达到缓存的设置的最大元素数时,该缓存维护对象并刷新最后访问的对象.多个线程访问缓存,并且在尝试删除旧元素时发生上述错误,因此可以将新元素添加到数组中.请参阅下面的一些代码段:
public class SlidingCache<TKey, TValue> : IDictionary<TKey, TValue>
{
public int MinCount { get; private set; }
public int MaxCount { get; private set; }
private readonly IDictionary<TKey, CacheValue> _cache = new ConcurrentDictionary<TKey, CacheValue>();
public SlidingCache(int minCount=75000, int maxCount=100000)
{
if (minCount <= 2)
throw new ArgumentException("minCount");
if (maxCount <= minCount)
throw new ArgumentException("maxCount");
MinCount = minCount;
MaxCount = maxCount;
}
#region IDictionary<TKey, TValue>
public int Count
{
get { return _cache.Count; }
}
public TValue this[TKey key]
{
get
{
return _cache[key].Value;
}
set
{
_cache[key]=new CacheValue(value);
RemoveExcess();
}
}
...
#endregion
private void RemoveExcess()
{
if (this.Count <= this.MaxCount || Interlocked.Increment(ref _removingExcess) != 1)
return;
ThreadPool.QueueUserWorkItem(RemoveExcessAsync, null);
}
private int _removingExcess;
private void RemoveExcessAsync(object state)
{
var remove = _cache.ToArray().OrderByDescending(i => i.Value.LastRequestTime).Take(MaxCount - MinCount);
foreach (var pair in remove)
{
_cache.Remove(pair.Key);
}
Interlocked.Exchange(ref _removingExcess, 0);
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释上述异常和任何变通办法的潜在原因吗?
谢谢.
ang*_*son 15
那是因为Enumerable.ToArray
并发集合使用是不安全的.
您应该将内部变量声明为类型ConcurrentDictionary
而不是IDictionary
,因为这将使用ToArray
字典本身实现的实现,而不是依赖于扩展方法:
private readonly IDictionary<TKey, CacheValue> _cache = new ConcurrentDictionary<TKey, CacheValue>();
Run Code Online (Sandbox Code Playgroud)
特别是,Enumerable.ToArray
最终在Buffer
内部使用类,这里是如何定义该类的构造函数(它的开头):
internal Buffer(IEnumerable<TElement> source) {
TElement[] items = null;
int count = 0;
ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null) {
count = collection.Count;
if (count > 0) {
items = new TElement[count];
collection.CopyTo(items, 0);
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,它使用Count
字典的属性,创建一个数组,然后将元素复制到数组中.如果基础词典在阅读之后Count
但在CopyTo
您遇到问题之前已经获得了至少一个其他项目.
您可以将其与ToArray
使用锁定的字典本身内部的实现进行对比:
(来自ConcurrentDictionary.cs - 参考源)
public KeyValuePair<TKey, TValue>[] ToArray()
{
int locksAcquired = 0;
try
{
AcquireAllLocks(ref locksAcquired);
int count = 0;
checked
{
for (int i = 0; i < m_tables.m_locks.Length; i++)
{
count += m_tables.m_countPerLock[i];
}
}
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count];
CopyToPairs(array, 0);
return array;
}
finally
{
ReleaseLocks(0, locksAcquired);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1229 次 |
最近记录: |