我收到的错误报告似乎来自以下代码:
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]; }
}
}
这是读取字典的调用代码:
public override Matrix GetCurrentBoneTransform(BonePose pose)
{
BoneKeyFrameCollection channel = base.AnimationInfo.AnimationChannels[pose.Name];
}
这是创建字典的代码,在启动时发生:
// 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));
}
}
错误是:
索引超出了数组的范围。
行:0
at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
在 Xclna.Xna.Animation.InterpolationController.GetCurrentBoneTransform(BonePose 姿态)
我原本期望KeyNotFoundException带有错误的键,但我得到“Index超出了数组的范围”。 我不明白我是如何从上面的代码中获得该异常的?
顺便说一下,代码是单线程的。
答案 0 :(得分:10)
“索引超出了数组的范围。错误在词典(或System.Collections
命名空间中的任何内容)上,当文档说不应该抛出错误时<由于违反线程安全而导致强> 总是 。
System.Collections
命名空间中的所有集合只允许两个操作中的一个发生
您必须使用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();
}
}
或将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;
}
更新:我真的不知道你所展示的代码是如何造成这种情况的。 Here is the source code表示导致它被抛出的函数。
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;
}
代码抛出ArgumentOutOfRangeException
的唯一方法是,如果您尝试将buckets
或entries
中的非法记录编入索引。
因为您的密钥是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
被调用的唯一地方是this[TKey key]
的设置器,公共Add
函数和OnDeserialization
内部。 buckets
被替换的唯一方法是,如果我们在FindEntry
调用期间在另一个线程上发生buckets[hashCode % buckets.Length]
调用的同时调用三个列出的函数之一。
我们唯一可以获得错误entries[i]
来电的方法是entries
是否被我们换出(遵循与buckets
相同的规则)或者我们得到{{{ 1}}。获取i
错误值的唯一方法是i
返回错误值。从entries[i].next
获取错误值的唯一方法是在entries[i].next
,Insert
或Resize
期间进行并发操作。
我唯一能想到的是Remove
调用出错了你在反序列化之前有错误的数据开始,或者OnDeserialization
有更多代码影响字典你没有向我们展示。