报错内容:
Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at P2PSocket.Server.P2PServer.<>c__DisplayClass13_1.<AcceptSocket_ClientName>b__6() in E:\启梦\源码\P2PSocket\20201020\src\P2PSocket.Server\P2PServer.cs:line 213
at P2PSocket.Core.Utils.EasyOp.Do(Action func, Action successHandle, Action`1 errorHandle) in E:\启梦\源码\P2PSocket\20201020\src\P2PSocekt.Core\Utils\EasyOp.cs:line 35
at P2PSocket.Server.P2PServer.<>c__DisplayClass13_0.<AcceptSocket_ClientName>b__1() in E:\启梦\源码\P2PSocket\20201020\src\P2PSocket.Server\P2PServer.cs:line 203
at P2PSocket.Core.Utils.EasyOp.Do(Action func, Action successHandle, Action`1 errorHandle) in E:\启梦\源码\P2PSocket\20201020\src\P2PSocekt.Core\Utils\EasyOp.cs:line 35
at P2PSocket.Server.P2PServer.AcceptSocket_ClientName(IAsyncResult ar) in E:\启梦\源码\P2PSocket\20201020\src\P2PSocket.Server\P2PServer.cs:line 188
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback()
at System.Net.ContextAwareResult.<>c.<Complete>b__15_0(Object s)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionCallback(Int32 numBytes, SocketError errorCode)
at System.Net.Sockets.AcceptOverlappedAsyncResult.CompletionCallback(IntPtr acceptedFileDescriptor, Byte[] socketAddress, Int32 socketAddressLen, SocketError errorCode)
at System.Net.Sockets.SocketAsyncContext.AcceptOperation.InvokeCallback(Boolean allowPooling)
at System.Net.Sockets.SocketAsyncContext.O
其实我们平时使用Dictionary无非就用Add、Remove这样的方法,根本没有考虑过内部实现的机制。在Dictionary内部为了维护Dictionary的功能和高效的特性,有自己的一些计数器和状态维护机制。Dictionary.Add方法实际上里头只有一句话:this.Insert(key, value, true);也就是最终的实现都是在Insert方法里的。再用Reflector扒开Insert方法里的内容看看:
private void Insert(TKey key, TValue value, bool add)
{
int freeList;
if (key == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (this.buckets == null)
{
this.Initialize(0);
}
int num = this.comparer.GetHashCode(key) & 0x7fffffff;
int index = num % this.buckets.Length;
for (int i = this.buckets[index]; i >= 0; i = this.entries[i].next)
{
if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key))
{
if (add)
{
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
this.entries[i].value = value;
this.version++;
return;
}
}
if (this.freeCount > 0)
{
freeList = this.freeList;
this.freeList = this.entries[freeList].next;
this.freeCount--;
}
else
{
if (this.count == this.entries.Length)
{
this.Resize();
index = num % this.buckets.Length;
}
freeList = this.count;
this.count++;
}
this.entries[freeList].hashCode = num;
this.entries[freeList].next = this.buckets[index];
this.entries[freeList].key = key;
this.entries[freeList].value = value;
this.buckets[index] = freeList;
this.version++;
}
在这里可以看到有大量的计数器存在,而我们再来倒回头看看最开始抛出的异常对象:IndexOutOfRangeException,如果计数器出错,相当有可能在使用计数器做下标时出现下标越界的情况。那么这是.Net的Bug么?
在上面引用MSDN的时候微软已经明确说了,在多线程访问的时候不要使用Dictionary而应该使用ConCurrentDictionay,利用ConCurrentDictionay里的TryAdd、TryUpdate方法来避免出现类似的错误。
其实多线程并发计算的时候,经常会出现计数错误的情况。
完全个人研究,有错希望大神纠正。也可留下您的联系方式,共同探讨
——————————————————————————————————
作者:Henny_CHN
转载请标明出处,原文地址:
https://blog.youkuaiyun.com/a1234012340a/article/details/109285308
如果感觉本文对您有帮助,请留下您的赞,您的支持是我坚持写作最大的动力,谢谢!