C# .net 集合-并发处理(List<t>集合换成ConcurrentQueue、ConcurrentDictionary )

背景
List集合,数组Int[],String[] ……,Dictory字典等等。但是这些列表、集合和数组的线程都不是安全的,不能接受并发请求。例如:

namespace Spider
{
    class Program
    {

        private static List<Product> _Products { get; set; }
        static void Main(string[] args)
        {

            _Products = new List<Product>();
            Task t1 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });
            Task t2 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });
            Task t3 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });

            Task.WaitAll(t1, t2, t3);//同步执行
            Console.WriteLine(_Products.Count);
            Console.ReadLine();
        }

        static void AddProducts()
        {
            Parallel.For(0, 1000, (i) =>
            {
                Product product = new Product();
                product.Name = "name" + i;
                product.Category = "Category" + i;
                product.SellPrice = i;
                _Products.Add(product);
            });

        }
    }

    class Product
    {
        public string Name { get; set; }
        public string Category { get; set; }
        public int SellPrice { get; set; }
    }
}

上图理论上是会显示3000行数据,但是实际上显示了2934个由于是list集合并不能保证线程安全,所以导致数据丢失,无法保证数据的一致性。这个时候我们常用的解决方法就是加锁(lock)lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

   static void Main(string[] args)
        {

            _Products = new List<Product>();
            Stopwatch swTask = new Stopwatch();//用于统计时间消耗的
            swTask.Start();
            Task t1 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });
            Task t2 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });
            Task t3 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });

            Task.WaitAll(t1, t2, t3);//同步执行
            swTask.Stop();
            Console.WriteLine("List<Product> 当前数据量为:" + _Products.Count);
            Console.WriteLine("List<Product> 执行时间为:" + swTask.ElapsedMilliseconds);

            Console.ReadLine();
        }

        static void AddProducts()
        {
            Parallel.For(0, 1000000, (i) =>
            {
                lock (_Products)
                {

                    Product product = new Product();
                    product.Name = "name" + i;
                    product.Category = "Category" + i;
                    product.SellPrice = i;
                    _Products.Add(product);
                }
                 //lock 是Monitor的语法糖
                //Monitor.Enter(_Products);
                //Product product = new Product();
                //product.Name = "name" + i;
                //product.Category = "Category" + i;
                //product.SellPrice = i;
                //_Products.Add(product);
                //Monitor.Exit(_Products);
            });

        }
List<Product> 当前数据量为:3000000
List<Product> 执行时间为:4638

这个时候就显示3000

但是锁的引入,带来了一定的开销和性能的损耗,并降低了程序的扩展性,而且还会有死锁的发生(虽说概率不大,但也不能不防啊),因此:使用LOCK进行并发编程显然不太适用。

还好,微软一直在更新自己的东西:

.NET Framework 4提供了新的线程安全和扩展的并发集合,它们能够解决潜在的死锁问题和竞争条件问题,因此在很多复杂的情形下它们能够使得并行代码更容易编写,这些集合尽可能减少使用锁的次数,从而使得在大部分情形下能够优化为最佳性能,不会产生不必要的同步开销。

需要注意的是:在串行代码中使用并发集合是没有意义的,因为它们会增加无谓的开销。

在.NET Framework4.0以后的版本中提供了命名空间:System.Collections.Concurrent 来解决线程安全问题,通过这个命名空间,能访问以下为并发做好了准备的集合。

1.BlockingCollection 与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。

2.ConcurrentBag 提供对象的线程安全的无序集合

3.ConcurrentDictionary 提供可有多个线程同时访问的键值对的线程安全集合

4.ConcurrentQueue 提供线程安全的先进先出集合

5.ConcurrentStack 提供线程安全的后进先出集合

这些集合通过使用比较并交换和内存屏障等技术,避免使用典型的互斥重量级的锁,从而保证线程安全和性能。

 static void Main(string[] args)
        {

            _Products = new ConcurrentQueue<Product>();
            Stopwatch swTask = new Stopwatch();//用于统计时间消耗的
            swTask.Start();
            Task t1 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });
            Task t2 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });
            Task t3 = Task.Factory.StartNew(() =>
            {
                AddProducts();
            });

            Task.WaitAll(t1, t2, t3);//同步执行
            swTask.Stop();
            Console.WriteLine("List<Product> 当前数据量为:" + _Products.Count);
            Console.WriteLine("List<Product> 执行时间为:" + swTask.ElapsedMilliseconds);

            Console.ReadLine();
        }

        static void AddProducts()
        {
            Parallel.For(0, 1000000, (i) =>
            {

                    Product product = new Product();
                    product.Name = "name" + i;
                    product.Category = "Category" + i;
                    product.SellPrice = i;
                    _Products.Enqueue(product);

            });

        }
    }
打印

List 当前数据量为:3000000
List 执行时间为:2911
“`
得出结果

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值