C# .net 集合-并发处理(List集合换成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 
“` 
得出结果


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864