List<T>方法调用线程同步问题
2013-04-15 13:04
267 查看
这是在调用List<T>的AddRange方法时,遇到“System.ArgumentException: 目标数组的长度不够。请检查 destIndex 和长度以及数组的下限。”的问题。
初步估计是线程安全的问题。
在搜索解决方法时看到的linjf520的一篇分析,原文地址如下:
http://bbs.csdn.net/topics/370224912
觉得它对这个问题的分析相对独特,故转过来了。
System.ArgumentException: 目标数组的长度不够。请检查 destIndex 和长度以及数组的下限。
在 System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
在 System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length)
在 System.Collections.Generic.List`1.ToArray()
这是怎么回事?
这里说说怎么避免这问题出现。
因为之前没有大量的对List<T>的测试,也不知道,List<T>是否线程安全的。当然线程安全的,
总得要有同步操作。这里肯定就会耗性能了。
List<T>.ToArray()出异常了? 我想:出于性能考虑,.net 的List<T>.ToArray()内的操作没有加锁同步处理。
如下代码解说:
C# code
?
以上代码是用.net refelctor 反编译的。List<T>.ToArray()方法内的代码。
果然是没有加lock或是其它的同步操作。
原因:有两操作A,B,分别异步的操作了一个.Add(T item)或是.Remove(T item)方法别一个List<T>的.ToArray()。
然后,在第一个先操作的,指令在时间片上,占第一位。
C# code
?
从上面来看,Add(T item)也是没有加同步锁的
if (this._size == this._items.Length)//A的
假设:
紧跟着下一条执行的指令应该是:
T[] destinationArray = new T[this._size];//B的
但,由于异步操作。
然后,在第二个操作的时候,因为是异步的,所以有可能在第一个操作的指令
紧跟着要执行的指定就成了第二个操作的第一个指令,依此类推下去,将所有执行顺序的列出来
//假设:执行前,this.Count == 10
if (this._size == this._items.Length)//A的
this.EnsureCapacity(this._size + 1);//A的
T[] destinationArray = new T[this._size];//B的 假设计this.Count = 10
this._items[this._size++] = item;//A的,这时,this.Count == 11了
Array.Copy(this._items, 0, destinationArray, 0, this._size);
//B的,这时,所有this.Count == 1,而刚定义的destinationArray.Length == 10的,
//所以这时,一旦执行COPY就会出下限不足的异常。
所以,现在我们知道了List不是线程安全的,很多时候,我们都必须自己要对List的操作前,加个锁,同步一下。
例如:
正常代码:
C# code
?
异常代码:
C# code
?
初步估计是线程安全的问题。
在搜索解决方法时看到的linjf520的一篇分析,原文地址如下:
http://bbs.csdn.net/topics/370224912
觉得它对这个问题的分析相对独特,故转过来了。
System.ArgumentException: 目标数组的长度不够。请检查 destIndex 和长度以及数组的下限。
在 System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
在 System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length)
在 System.Collections.Generic.List`1.ToArray()
这是怎么回事?
这里说说怎么避免这问题出现。
因为之前没有大量的对List<T>的测试,也不知道,List<T>是否线程安全的。当然线程安全的,
总得要有同步操作。这里肯定就会耗性能了。
List<T>.ToArray()出异常了? 我想:出于性能考虑,.net 的List<T>.ToArray()内的操作没有加锁同步处理。
如下代码解说:
C# code
?
果然是没有加lock或是其它的同步操作。
原因:有两操作A,B,分别异步的操作了一个.Add(T item)或是.Remove(T item)方法别一个List<T>的.ToArray()。
然后,在第一个先操作的,指令在时间片上,占第一位。
C# code
?
if (this._size == this._items.Length)//A的
假设:
紧跟着下一条执行的指令应该是:
T[] destinationArray = new T[this._size];//B的
但,由于异步操作。
然后,在第二个操作的时候,因为是异步的,所以有可能在第一个操作的指令
紧跟着要执行的指定就成了第二个操作的第一个指令,依此类推下去,将所有执行顺序的列出来
//假设:执行前,this.Count == 10
if (this._size == this._items.Length)//A的
this.EnsureCapacity(this._size + 1);//A的
T[] destinationArray = new T[this._size];//B的 假设计this.Count = 10
this._items[this._size++] = item;//A的,这时,this.Count == 11了
Array.Copy(this._items, 0, destinationArray, 0, this._size);
//B的,这时,所有this.Count == 1,而刚定义的destinationArray.Length == 10的,
//所以这时,一旦执行COPY就会出下限不足的异常。
所以,现在我们知道了List不是线程安全的,很多时候,我们都必须自己要对List的操作前,加个锁,同步一下。
例如:
正常代码:
C# code
?
C# code
?
相关文章推荐
- List<T>方法调用线程同步问题
- Android WEBVIEW中调用<a href>的问题!!!不是js的方法
- Java中泛型集合List<T>反序列化问题及解决方法
- scala调用java的方法,返回了一个对象链表List<Student>,在scala中遍历该链表获取指定Student的名字name
- 解决 ”不允许在查询中显式构造实体类型“问题及使用其他方法实现返回 List<Model对象>或者IQueryable<Model对象>对象
- net.sf.ezmorph.bean.MorphDynaBean cannotbe cast to xxx 关于JSON对象解析List<XXX>在调用时出错问题
- 关于List.addAll(Collection<E>)方法遇到的问题
- C#中泛型集合List<T>反序列化问题及解决方法
- 黄聪:用ForEach方法对List<T>中对每一个项目调用同一个方法
- C# List<T> Add方法循环添加时只保存了最后一次的数据(覆盖问题)
- 谈谈Dictionary<T1,T2>和List<T>的问题
- DataGridView和List<T>绑定不显示问题
- [异常]List<T>.Sort()返回在Compare时出错:IComparer (或其依赖的 IComparable 方法)未返回零
- 关于TreeSet<E>泛型类中的compareTo()方法的调用
- 根据部分属性判断List<实体bean>中是否含有某个实体bean-重写equals方法
- int数组转化成List<Integer>简便的方法
- C#中将DataTable转化成List<T>的方法解析
- List<Map>自定义Comparator在jdk1.7中出现问题
- “无法更新EntitySet“*****”,因为它有一个DefiningQuery,而<ModificationFunctionMapping>元素中没有支持当前操作的<InsertFunction>元素”问题的解决方法
- Java中使用hql,sql查询返回的list<Object> 转成需要的实体对象--方法讲解!