我收到了一个错误报告,似乎来自以下代码:
public class AnimationChannelCollection : ReadOnlyCollection<BoneKeyFrameCollection>
{
private Dictionary<string, BoneKeyFrameCollection> dict =
new Dictionary<string, BoneKeyFrameCollection>();
private ReadOnlyCollection<string> affectedBones;
// This immutable data structure should not be created by the library user
internal AnimationChannelCollection(IList<BoneKeyFrameCollection> channels)
: base(channels)
{
// Find the affected bones
List<string> affected = new List<string>();
foreach (BoneKeyFrameCollection frames in channels)
{
dict.Add(frames.BoneName, frames);
affected.Add(frames.BoneName);
}
affectedBones = new ReadOnlyCollection<string>(affected);
}
public BoneKeyFrameCollection this[string boneName]
{
get { return dict[boneName]; }
}
}
Run Code Online (Sandbox Code Playgroud)
这是读取字典的调用代码:
public override Matrix GetCurrentBoneTransform(BonePose pose)
{
BoneKeyFrameCollection channel = base.AnimationInfo.AnimationChannels[pose.Name];
}
Run Code Online (Sandbox Code Playgroud)
这是创建字典的代码,在启动时发生:
// Reads in processed animation info written in the pipeline
internal sealed class AnimationReader : ContentTypeReader<AnimationInfoCollection>
{
/// <summary>
/// Reads in an XNB stream and converts it to a ModelInfo object
/// </summary>
/// <param name="input">The stream from which the data will be read</param>
/// <param name="existingInstance">Not used</param>
/// <returns>The unserialized ModelAnimationCollection object</returns>
protected override AnimationInfoCollection Read(ContentReader input, AnimationInfoCollection existingInstance)
{
AnimationInfoCollection dict = new AnimationInfoCollection();
int numAnimations = input.ReadInt32();
/* abbreviated */
AnimationInfo anim = new AnimationInfo(animationName, new AnimationChannelCollection(animList));
}
}
Run Code Online (Sandbox Code Playgroud)
错误是:
指数数组的边界之外.
行:0
在System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
在System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Xclna.Xna.Animation.InterpolationController.GetCurrentBoneTransform(BonePose pose)
我本来期望KeyNotFoundException有错误的键,但我得到"索引超出了数组的范围".我不明白我是如何从上面的代码中获得该异常的?
顺便说一下,代码是单线程的.
Sco*_*ain 11
阿"索引阵列的边界之外." System.Collections当文档说不应该抛出错误时,字典(或命名空间中的任何内容)上的错误总是由于违反线程安全性而导致.
System.Collections命名空间中的所有集合仅允许两个操作中的一个发生
您必须使用a来同步对字典的所有访问,ReaderWriterLockSlim这将提供上述确切的行为
private Dictionary<string, BoneKeyFrameCollection> dict =
new Dictionary<string, BoneKeyFrameCollection>();
private ReaderWriterLockSlim dictLock = new ReaderWriterLockSlim();
public BoneKeyFrameCollection this[string boneName]
{
get
{
try
{
dictLock.EnterReadLock();
return dict[boneName];
}
finally
{
dictLock.ExitReadLock();
}
}
}
public void UpdateBone(string boneName, BoneKeyFrameCollection col)
{
try
{
dictLock.EnterWriteLock();
dict[boneName] = col;
}
finally
{
dictLock.ExitWriteLock();
}
}
Run Code Online (Sandbox Code Playgroud)
或者替换你Dictionary<string, BoneKeyFrameCollection>的ConcurrentDictionary<string, BoneKeyFrameCollection>
private ConcurrentDictionary<string, BoneKeyFrameCollection> dict =
new ConcurrentDictionary<string, BoneKeyFrameCollection>();
public BoneKeyFrameCollection this[string boneName]
{
get
{
return dict[boneName];
}
}
public void UpdateBone(string boneName, BoneKeyFrameCollection col)
{
dict[boneName] = col;
}
Run Code Online (Sandbox Code Playgroud)
更新:我真的不明白你所展示的代码是如何造成这种情况的.这是导致它被抛出的函数的源代码.
private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
Run Code Online (Sandbox Code Playgroud)
代码抛出a的唯一方法ArgumentOutOfRangeException是,如果您尝试索引buckets或中的非法记录entries.
因为您的密钥是a string并且字符串是不可变的,所以我们可以排除hashcode在将密钥放入字典后更改的密钥的值.剩下的就是buckets[hashCode % buckets.Length]打电话和几个entries[i]电话.
唯一buckets[hashCode % buckets.Length]可能失败的方法是buckets在buckets.Length属性调用和this[int index]索引器调用之间替换实例.唯一的一次buckets被替换时Resize在内部被称为Insert,Initialize由构造/第一个电话打电话Insert,或到一个呼叫OnDeserialization.
唯一Insert被调用的地方是setter for this[TKey key],public Addfunction和inside OnDeserialization.buckets要替换的唯一方法是,如果我们在调用FindEntry期间在另一个线程上发生调用的同一时间调用三个列出的函数之一buckets[hashCode % buckets.Length].
我们唯一可以得到一个糟糕的entries[i]电话的方法是,如果entries我们换掉了(遵循相同的规则buckets),或者我们得到一个糟糕的价值i.获得错误值的唯一方法i是if entries[i].next返回错误的值.从得到一个坏值的唯一方法entries[i].next是有并发操作时怎么回事Insert,Resize或者Remove.
我唯一能想到的是要么在OnDeserialization调用时出错,要么在反序列化之前有错误的数据,或者有更多的代码AnimationChannelCollection会影响你没有向我们展示的字典.