对非线程安全类List<T>的一些总结
一个项目的一个功能点,需要从接口接受返回数据,并对返回的数据进行一些业务处理,处理完成之后,添加到一个List<T>中,然后在View中循环这个List<T>,展示所有的数据。每次从接口中取回的数据量不等,最多会有上百条。虽说上百条也不算多,但是每条数据都要经过一系列的业务处理,感觉这样也挺耗时的,于是考虑使用Parallel.Foreach来进行并行处理。
项目完成之后,对比了一下并行和非并行的情况,发现并行之后并没有提高多少效能,倒是遇到了一些比较怪异的问题。
Parallel.Foreach 中对List<T>执行Add操作之后,List<T>的Count有时候并不是执行并行的操作的执行次数,而且List<T>中会有Item为null的情况。其实这个问题的解决方案很简单,就是因为List<T>不是线程安全的类,在多线程情况下就会导致一些不可预知的情况,加个锁就可以解决问题了。但是,如果能更好的了解到底是什么原因导致的,岂不更好,于是在一个同事的帮助下,找到了List<T>的源码(感谢微软开放了源码,地址:http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646),从List<T>的源码中更进一步的了解了导致以上问题的原因。
在分析上面两个问题之前,我们先了解一下List<T>的内部情况。从源码中我们可以看到List<T>是通过一个Array来进行处理的,如果初始没有对List<T>设置容量,List<T>容量将为0,如果此时使用Add添加新项的时候,就会给List<T>设置一个初始容量(初始值为4)。使用Add添加新项的时候,如果已经达到容量最大值,List<T>会自动扩充容量的值,扩充后的容量的值为原来既有项目数量的2倍(其实也就是原来容量的2倍)。
我们把Add方法和扩容方法摘抄如下:
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + );
_items[_size++] = item;
_version++;
}
private void EnsureCapacity(int min) {
if (_items.Length < min) {
int newCapacity = _items.Length == ? _defaultCapacity : _items.Length * ;
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
if (newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
}
了解了List<T>内部内部扩容情况之后,下面就以上两个问题进行分析。
1、 List<T>中的Item数量比预期的少。
导致这个问题的原因其实还是挺明显的。当两个线程(ThreadA和TreadB),同时调用Add方法添加不同的值的时候,如果此时ThreadA和ThreadB获取到的size相同,就会出现下面这种情况:
ThreadA:List<T>[size] = A;
ThreadB:List<T>[size] = B;
这种情况下,在size这个位置只会有一个ThreadB设置的值,ThreadA设置的值将会被替换掉,这也就是造成Item数量比预期少的原因。
2、 List<T>中的Item有null。
其实和上面类似,看Add中的代码:
_items[_size++] = item;
我们改变一下,变成:
(1)_size = _size+1;
(2)Items[_size] = item;
如果ThreadA执行完(1)之后ThreadB获取到新的_size也执行了(1)那此时_size就相当于是加2了,所以_size+1索引位置的项就是T的默认值了(值类型会值类型的默认值,引用类型为null)。这样就能解释为什么会出现null的原因了。
其实这两个问题完全就是同一个问题,只不过表象不同而已。最终解决方案很简单,要么自己加锁,要么使用线程安全的ConcurrentBag<T>。
对非线程安全类List<T>的一些总结的更多相关文章
- Java线程:线程安全类和Callable与Future(有返回值的线程)
一.线程安全类 当一个类已经很好的同步以保护它的数据时,这个类就称为线程安全的.当一个集合是安全的,有两个线程在操作同一个集合对象,当第一个线程查询集合非空后,删除集合中所有元素的时候,第二个线程也来 ...
- Netty的并发编程实践4:线程安全类的应用
在JDK1.5的发行版本中,Java平台新增了java.util.concurrent,这个包中提供了一系列的线程安全集合.容器和线程池,利用这些新的线程安全类可以极大地降低Java多线程编程的难度, ...
- 那些年读过的书《Java并发编程实战》二、如何设计线程安全类
1.设计线程安全类的过程 设计线程安全类的过程就是设计对象状态并发访问下线程间的协同机制(在不破坏对象状态变量的不变性条件的前提下). (1)构建线程安全类的三个基本要素: 1)找出构成对象状态的所有 ...
- 非线程安全的HashMap 和 线程安全的ConcurrentHashMap
在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性.在系统运行中,使用到这些缓存数据,都可以直 ...
- 【转】php Thread Safe(线程安全)和None Thread Safe(NTS,非 线程安全)之分
Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. ...
- JAVA中的线程安全与非线程安全
原文:http://blog.csdn.net/xiao__gui/article/details/8934832 ArrayList和Vector有什么区别?HashMap和HashTable有什么 ...
- StringBuffer(线程安全)StringBuilder(非线程安全)
StringBuffer属于线程安全,相对为重量级 StringBuilder属于非线程安全,相对为轻量级 线程安全的概念: 网络编程中许多线程可能会同时运行一段代码.当每次运行结果和单独线程运行的结 ...
- PHP 线程安全与非线程安全版本的区别深入解析
Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍 ...
- Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分
Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. ...
随机推荐
- 非阻塞同步算法实战(二)-BoundlessCyclicBarrier
本人是本文的作者,首发于ifeve(非阻塞同步算法实战(二)-BoundlessCyclicBarrier) 前言 相比上一 篇而言,本文不需要太多的准备知识,但技巧性更强一些.因为分析.设计的过程比 ...
- Modern C++ CHAPTER 2(读书笔记)
CHAPTER 2 Recipe 2-1. Initializing Variables Recipe 2-2. Initializing Objects with Initializer Lists ...
- ceph实践: 搭建环境
作者:吴香伟 发表于 2014/09/26 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 本节主要参考官网的ADDING/REMOVING OSDS章节. 同步 ...
- 一个CURL
CURL curl是利用url语法规则来传输文件,数据的工具,主要实现和服务器中网页,资源传输的工具.这里有一个关于感冒还没好的段子. 1.去phpini开启curl扩展,重启阿帕奇或者配置环境变量或 ...
- phantomjs和angular-seo-server实现angular单页面seo
1.下载phantomjs,并配置环境变量为 eg:E:\phantomjs-2.1.1-windows\bin 2.下载angular-seo-server 3.windows下:cmd eg: ...
- Jquery实现文字向上逐条滚动
直接上代码: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" ...
- Java正则表达式的解释说明
1.字符x 字符 x.例如a表示字符a\\ 反斜线字符.在书写时要写为\\\\.(注意:因为java在第一次解析时,把\\\\解析成正则表达式\\,在第二次解析时再解析为\,所以凡是不是1 ...
- IIS出现 分析器错误消息: 在应用程序级别之外使用注册为 allowDefinition='MachineToApplication' 的节是错误的
这是因为发布的时候按了“生成部署包”
- 【洛谷P2737】Beef McNuggets
首先有这样一个结论:若p,q为自然数,且gcd(p,q)=1,那么px+qy不能表示的最大数为pq-p-q 那么本题中p,q均取决于最大的两个数,不妨取256,那么上界为256^2-256*2 之后就 ...
- 使用WindowsPE破解管理员密码
使用WindowsPE破解管理员密码 1将操作系统关闭,编辑虚拟机设置,先用老毛桃生成一个ISO,点.iso制作,再点生成ISO文件,然后选择WinPE的ISO镜像存放的位置 将WinPE.ISO文件 ...