List是线程安全的吗?如果不是该怎么办呢?安全的List对性能的影响有多大呢?

测试条件:

开启2个并行执行任务,往同一个list对象写入值

测试代码:

static int maxNum = 1000000;

        static List<int> list = new List<int>();

        static void Main(string[] args)

        {

            //迭代次数

            int iterationNum = 3;

            CodeTimer.Initialize();

            CodeTimer.Time("List是否是线程安全的呢?", iterationNum, new Action(ListIsThreadSafe));

            //Console.Write(sbIsThreadSafe.ToString());

            Console.Read();

        }

        private static void ListIsThreadSafe()

        {

            Parallel.For(1, maxNum / 2, (i) =>

            {

                list.Add(i);

            });

            Parallel.For(maxNum / 2 + 1, maxNum, (i) =>

            {

                list.Add(i);

            });

        }

测试结果:

测试结论:

之所以会造成以上的结果是因为list对象不是线程安全。那该怎么办呢?

这时我们需要使用System.Collections.Concurrent命名空间下的类型来用于并行循环体内。



说明


BlockingCollection<T>


为实现 IProducerConsumerCollection<T> 的线程安全

集合提供阻止和限制功能。


ConcurrentBag<T>


表示对象的线程安全的无序集合。


ConcurrentDictionary<TKey, TValue>


表示可由多个线程同时访问的键值对的线程安全集合。


ConcurrentQueue<T>


表示线程安全的先进先出 (FIFO) 集合。


ConcurrentStack<T>


表示线程安全的后进先出 (LIFO) 集合。


OrderablePartitioner<TSource>


表示将一个可排序数据源拆分成多个分区的特定方式。


Partitioner


提供针对数组、列表和可枚举项的常见分区策略。


Partitioner<TSource>


表示将一个数据源拆分成多个分区的特定方式。

我们再来进行一次测试。

测试代码:

static int maxNum = 1000000;   

        static ConcurrentQueue<int> safeList = new ConcurrentQueue<int>();

        static void Main(string[] args)

        {

            //迭代次数

            int iterationNum = 3;

            CodeTimer.Initialize();

            CodeTimer.Time("ConcurrentQueue是否是线程安全的呢?", iterationNum, new Action(ListIsThreadSafe));

            //Console.Write(sbIsThreadSafe.ToString());

            Console.Read();

        }

        private static void ListIsThreadSafe()

        {

            Parallel.For(1, maxNum / 2, (i) =>

            {

                safeList.Enqueue(i);

            });

            Parallel.For(maxNum / 2 + 1, maxNum, (i) =>

            {

                safeList.Enqueue(i);

            });

        }

测试结果:

测试结论:

ConcurrentQueue是线程安全的。在遇到多线程和并行开发时应该使用ConcurrentQueue而不是List.

疑问:为了确保对象线程安全,系统底层实现机制必定是要使用对象加锁和解锁的功能来确保对象安全,那么加锁和解锁是否会对性能造成影响呢?

我们再来进行一组测试:

测试条件:

(1)对100万个数据进行普通的循环运算然后添加到List集合对象中。

(2)对100万个数据进行并行循环运算然后添加到ConcurrentQueue集合对象中。

测试代码:

static int maxNum = 1000000;

        static List<BigInteger> list = new List<BigInteger>();

        static ConcurrentQueue<BigInteger> safeList = new ConcurrentQueue<BigInteger>();

        static void Main(string[] args)

        {

            //迭代次数

            int iterationNum = 3;

            CodeTimer.Initialize();

            CodeTimer.Time("普通的循环运算", iterationNum, new Action(NormalCompute));

            CodeTimer.Time("并行循环运算_1", iterationNum, new Action(ParallelCompute_1));

            CodeTimer.Time("并行循环运算_2", iterationNum, new Action(ParallelCompute_2));

            Console.Read();

        }

        private static void NormalCompute()

        {

            for (int i = 1; i <= maxNum; i++)

            {

                Math.Pow(i, i + 1);

                list.Add(new BigInteger(i));

            }

        }

        private static void ParallelCompute_1()

        {

            Parallel.For(1, maxNum, (i) =>

                {

                    Math.Pow(i, i + 1);

                    safeList.Enqueue(new BigInteger(i));

                });

        }

        private static void ParallelCompute_2()

        {

            Parallel.For(1, maxNum / 2, (i) =>

            {

                Math.Pow(i, i + 1);

                safeList.Enqueue(new BigInteger(i));

            });

            Parallel.For(maxNum / 2 + 1, maxNum, (i) =>

            {

                Math.Pow(i, i + 1);

                safeList.Enqueue(new BigInteger(i));

            });

        }

测试结果:

 测试结论:

和我预期的结论是一样的,并行方式所耗费的时间更加的长。线程安全的加锁和解锁对性能的影响还是比较大的。

时间: 2024-08-29 03:59:20

List是线程安全的吗?如果不是该怎么办呢?安全的List对性能的影响有多大呢?的相关文章

NHibernate中Session的处理 线程不安全

NHibernate中Session是线程不安全的,而且每次数据库操作 请求创建Session时对性能有些影响.在Windows应用中可以通过 [ThreadStatic]特性很简单的就可以实现线程安全,而在Web中可以通过将Session与用于请求HttpContext绑定实现线程安全,并且用户当前请求时只有一个Session.代码如下: ISessionManage.cs using System;using NHibernate; namespace Commercial.Jwsoft.F

Java线程并发中常见的锁--自旋锁 偏向锁

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocking pac

Java线程并发中常见的锁

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocking pac

iOS应用性能调优的25个建议和技巧

目录 我要给出的建议将分为三个不同的等级: 入门级. 中级和进阶级: 入门级(这是些你一定会经常用在你app开发中的建议) 1. 用ARC管理内存 2. 在正确的地方使用reuseIdentifier 3. 尽可能使Views透明 4. 避免庞大的XIB 5. 不要block主线程 6. 在Image Views中调整图片大小 7. 选择正确的Collection 8. 打开gzip压缩 中级(这些是你可能在一些相对复杂情况下可能用到的) 9. 重用和延迟加载Views 10. Cache, C

MySQL配置文件mysql.ini参数详解、MySQL性能优化

MySQL配置文件mysql.ini参数详解.MySQL性能优化 my.ini(Linux系统下是my.cnf),当mysql服务器启动时它会读取这个文件,设置相关的运行环境参数. my.ini分为两块:Client Section和Server Section.   Client Section用来配置MySQL客户端参数.   要查看配置参数可以用下面的命令: show variables like '%innodb%'; # 查看innodb相关配置参数 show status like

iOS每日一记之——————————iOS性能优化

写在前面 本文来自iOS Tutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序员.这是他的个人网站:http://www.marcelofabri.com/,你还可以在Twitter上关注@marcelofabri_. 性能对 iOS 应用的开发尤其重要,如果你的应用失去反应或者很慢,失望的用户会把他们的失望写满App Store的评论.然而由于iOS设备的限制,有时搞好性能是一件难事.开发过程中你会有很多需要注意的事项,你也很容易在做出选择时忘记考虑

iOS开发-iOS应用性能调优的25个建议和技巧

我要给出的建议将分为三个不同的等级: 入门级. 中级和进阶级: 入门级(这是些你一定会经常用在你app开发中的建议) 1. 用ARC管理内存 2. 在正确的地方使用reuseIdentifier 3. 尽可能使Views不透明 4. 避免庞大的XIB 5. 不要block主线程 6. 在Image Views中调整图片大小 7. 选择正确的Collection 8. 打开gzip压缩 中级(这些是你可能在一些相对复杂情况下可能用到的) 9. 重用和延迟加载Views 10. Cache, Cac

优化iOS程序性能的25个方法

1. 用ARC管理内存 ARC(Automatic ReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露.它自动为你管理retain和release的过程,所以你就不必去手动干预了.忘掉代码段结尾的release简直像记得吃饭一样简单.而ARC会自动在底层为你做这些工作.除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存. 现在所有的iOS程序都用ARC了,这条可以忽略. 2. 在

iOS 25个性能优化/内存优化常用方法

1. 用ARC管理内存 ARC(Automatic ReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露.它自动为你管理retain和release的过程,所以你就不必去手动干预了.忘掉代码段结尾的release简直像记得吃饭一样简单.而ARC会自动在底层为你做这些工作.除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存. 2. 在正确的地方使用 reuseIdentifier 一