第十四节: 介绍四大并发集合类并结合单例模式下的队列来说明线程安全和非安全的场景及补充性能调优问题。

一. 四大并发集合类

背景:我们目前使用的所有集合都是线程不安全的 。

  A. ConcurrentBag:就是利用线程槽来分摊Bag中的所有数据,链表的头插法,0代表移除最后一个插入的值.

  (等价于同步中的List)

  B. ConcurrentStack:线程安全的Stack是使用Interlocked来实现线程安全, 而没有使用内核锁.

  (等价于同步中的数组)

  C. ConcurrentQueue: 队列的模式,先进先出

  (等价于同步中的队列)

  D. ConcurrentDictionary: 字典的模式

  (等价于同步中的字典)

以上四种安全的并发集合类,也可以采用同步版本+Lock锁(或其它锁)来实现

 代码实践:

 

          01-ConcurrentBag{Console.WriteLine("---------------- 01-ConcurrentBag ---------------------");ConcurrentBag<int> bag = new ConcurrentBag<int>();bag.Add(1);bag.Add(2);bag.Add(33);//链表的头插法,0代表移除最后一个插入的值var result = 0;//flag为true,表示移除成功,并且返回被移除的值var flag = bag.TryTake(out result);Console.WriteLine("移除的值为:{0}", result);}#endregion02-ConcurrentStack{Console.WriteLine("---------------- 02-ConcurrentStack ---------------------");ConcurrentStack<int> stack = new ConcurrentStack<int>();stack.Push(1);stack.Push(2);stack.Push(33);//链表的头插法,0代表移除最后一个插入的值var result = 0;//flag为true,表示移除成功,并且返回被移除的值var flag = stack.TryPop(out result);Console.WriteLine("移除的值为:{0}", result);}#endregion03-ConcurrentQueue{Console.WriteLine("---------------- 03-ConcurrentQueue ---------------------");ConcurrentQueue<int> queue = new ConcurrentQueue<int>();queue.Enqueue(1);queue.Enqueue(2);queue.Enqueue(33);//队列的模式,先进先出,0代表第一个插入的值var result = 0;//flag为true,表示移除成功,并且返回被移除的值var flag = queue.TryDequeue(out result);Console.WriteLine("移除的值为:{0}", result);}#endregion04-ConcurrentDictionary{Console.WriteLine("---------------- 04-ConcurrentDictionary ---------------------");ConcurrentDictionary<int, int> dic = new ConcurrentDictionary<int, int>();dic.TryAdd(1, 10);dic.TryAdd(2, 11);dic.TryAdd(3, 12);dic.ContainsKey(3);//下面是输出字典中的所有值foreach (var item in dic){Console.WriteLine(item.Key + item.Value);}}#endregion        

复制代码

代码结果:

 

二. 队列的综合案例

   上面介绍了四大安全线程集合类和与其对应的不安全的线程集合类,可能你会比较疑惑,到底怎么安全了,那些不安全的集合类怎么能变成安全呢,下面以队列为例,来解决这些疑惑。

  1. 测试Queue队列并发情况下是不安全的(存在资源竞用的问题),ConcurrentQueue队列在并发情况下是安全的。

  2. 利用Lock锁+Queue队列,实现多线程并发情况下的安全问题,即等同于ConcurrentQueue队列的效果。

    经典案例测试:开启100个线程进行入队操作,正常所有的线程执行结束后,队列中的个数应该为100.

    ①. Queue不加锁的情况:结果出现99、98、100,显然是出问题了。

复制代码

            {Queue queue = new Queue();object o = new object();int count = 0;List<Task> taskList = new List<Task>();for (int i = 0; i < 100; i++){var task = Task.Run(() =>{queue.Enqueue(count++);});taskList.Add(task);}Task.WaitAll(taskList.ToArray());//发现队列个数在不加锁的情况下 竟然不同 有100,有99Console.WriteLine("Queue不加锁的情况队列个数" + queue.Count);}

复制代码

    ②. Queue加锁的情况:结果全是100,显然是正确的。

复制代码

 1             {2                 Queue queue = new Queue();3                 object o = new object();4                 int count = 0;5                 List<Task> taskList = new List<Task>();6                 for (int i = 0; i < 100; i++)7                 {8                     var task = Task.Run(() =>9                     {
10                         lock (o)
11                         {
12                             queue.Enqueue(count++);
13                         }
14                     });
15                     taskList.Add(task);
16                 }
17 
18                 Task.WaitAll(taskList.ToArray());
19                 //发现队列个数在全是100
20                 Console.WriteLine("Queue加锁的情况队列个数" + queue.Count);
21             }

复制代码

    ③. ConcurrentQueue不加锁的情况:结果全是100,显然是正确,同时证明ConcurrentQueue队列本身就是线程安全的。

复制代码

 1             {2                 ConcurrentQueue<int> queue = new ConcurrentQueue<int>();3                 object o = new object();4                 int count = 0;5                 List<Task> taskList = new List<Task>();6 7                 for (int i = 0; i < 100; i++)8                 {9                     var task = Task.Run(() =>
10                     {
11                         queue.Enqueue(count++);
12                     });
13                     taskList.Add(task);
14                 }
15                 Task.WaitAll(taskList.ToArray());
16                 //发现队列个数不加锁的情形=也全是100,证明ConcurrentQueue是线程安全的
17                 Console.WriteLine("ConcurrentQueue不加锁的情况下队列个数" + queue.Count);
18             }

复制代码

  3. 在实际项目中,如果使用队列来实现一个业务,该队列需要是全局的,这个时候就需要使用单例(ps:单例是不允许被实例化的,可以通过单例类中的属性或者方法的形式来获取这个类),同时,队列的入队和出队操作,如果使用Queue队列,需要配合lock锁,来解决多线程下资源的竞用问题。

  经典案例:开启100个线程对其进行入队操作,然后主线程输入队列的个数,并且将队列中的内容输出.

  结果:队列的个数为100,输出内容1-100依次输出。

复制代码

 1 /// <summary>2     /// 单例类3     /// </summary>4     public class QueueUtils5     {6         /// <summary>7         /// 静态变量:由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次8         /// </summary>9         private static readonly QueueUtils _QueueUtils = new QueueUtils();
10 
11         /// <summary>
12         /// 声明为private类型的构造函数,禁止外部实例化
13         /// </summary>
14         private QueueUtils()
15         {
16 
17         }
18         /// <summary>
19         /// 声明属性,供外部调用,此处也可以声明成方法
20         /// </summary>
21         public static QueueUtils instanse
22         {
23             get
24             {
25                 return _QueueUtils;
26             }
27         }
28 
29 
30         //下面是队列相关的
31         Queue queue = new Queue();
32 
33         private static object o = new object();
34 
35         public  int getCount()
36         {
37             return queue.Count;
38         }
39 
40         /// <summary>
41         /// 入队方法
42         /// </summary>
43         /// <param name="myObject"></param>
44         public void Enqueue(object myObject)
45         {
46             lock (o)
47             {
48                 queue.Enqueue(myObject);
49             }
50         }
51         /// <summary>
52         /// 出队操作
53         /// </summary>
54         /// <returns></returns>
55         public object Dequeue()
56         {
57             lock (o)
58             {
59                 if (queue.Count > 0)
60                 {
61                     return queue.Dequeue();
62                 }
63             }
64             return null;
65         }
66 
67     }

复制代码

复制代码

 1   {2                 int count = 1;3                 List<Task> taskList = new List<Task>();4                 for (int i = 0; i < 100; i++)5                 {6                     var task = Task.Run(() =>7                     {8                         QueueUtils.instanse.Enqueue(count++);9                     });
10                     taskList.Add(task);
11                 }
12 
13                 Task.WaitAll(taskList.ToArray());
14                 //发现队列个数在全是100
15                 Console.WriteLine("单例模式下队列个数" + QueueUtils.instanse.getCount());
16 
17                 //下面是出队相关的业务
18                 while (QueueUtils.instanse.getCount() > 0)
19                 {
20                     Console.WriteLine("出队:" + QueueUtils.instanse.Dequeue());
21                 }
22             }

复制代码

。。。。。。。。。。。

三. 常见的几类性能调优

PS: 

1. 常见的一级事件:CPU占用过高、死锁问题、内存爆满
  a. CPU过高:查看是否while(true)中的业务过于复杂,导致cpu一直在高负荷运行。
  b. 死锁问题:乱用lock,千万不要lock中再加lock,多个lock重叠
  c. 内存爆满:字符串的无限增长,全局的静态变量过多。
2. 补充几个常用的性能调优的方式
  a. 使用字典类型Dictionary<T,T>,代替只有两个属性的对象或匿名对象。
  b. 使用数组代替只有两个属性的对象或匿名对象。
  比如:
    index:存放id
    value:存放数量或其他属性
3. 返璞归真,使用最原始的代码代替简洁漂亮的代码。
4. 合理的使用多线程,业务复杂的尽可能的并发执行(或者异步)。
5. 运用设计模式,使代码简洁、易于扩展。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438242.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十五节:深入理解async和await的作用及各种适用场景和用法

一. 同步VS异步 1. 同步 VS 异步 VS 多线程 同步方法&#xff1a;调用时需要等待返回结果&#xff0c;才可以继续往下执行业务 异步方法&#xff1a;调用时无须等待返回结果&#xff0c;可以继续往下执行业务 开启新线程&#xff1a;在主线程之外开启一个新的线程去执行业…

@PostConstruct注解

PostConstruct是Java自己的注解. PostConstruct该注解被用来修饰一个非静态的void()方法. PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次. PostConstruct在构造函数之后执行,init()方法之前执行. 执行顺序 Constructor >> Autow…

springCloud五大组件--Gateway

SpringCloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Spring Cloud Gateway 的目标&#…

多线程篇(被替换)

一. 背景 在刚接触开发的头几年里&#xff0c;说实话&#xff0c;根本不考虑多线程的这个问题&#xff0c;貌似那时候脑子里也有没有多线程的这个概念&#xff0c;所有的业务都是一个线程来处理&#xff0c;不考虑性能问题&#xff0c;当然也没有考虑多线程操作一条记录存在的并…

springCloud五大组件--Eureka

Spring Cloud 支持了 Zookeeper、Consul 和 Eureka&#xff0c;官方推荐 Eureka。 C(一致性)A(高可用)P(分区容错)理论&#xff0c;Eureka的选择就是放弃C&#xff0c;选择AP。 Eureka 采用纯 Java 实现&#xff0c;除实现了注册中心基本的服务注册和发现之外&#xff0c;极大…

.NET异步程序设计之任务并行库

目录 1.简介2.Parallel类 2.0 Parallel类简介2.1 Parallel.For()2.2 Parallel.ForEach()2.3 Parallel.Invoke()2.4 补充&#xff1a;线程安全集合3.Task类 3.0 Task类简介3.1 创建无返回值的Task任务3.2 创建有返回值的Task任务3.3 为Task添加延续任务3.4 Task.Delay3.5 Task对…

Mysql主从延时解决办法

1.忍受大法 第一种解决办法&#xff0c;很简单&#xff0c;无他&#xff0c;不管他&#xff0c;没有读到也没事。这时业务不需要任何改造&#xff0c;你好&#xff0c;我好&#xff0c;她也好~ 如果业务对于数据一致性要求不高&#xff0c;我们就可以采用这种方案。 2.数据同…

关于C#程序的单元测试

目录 1.单元测试概念2.单元测试的原则3.单元测试简单示例4.单元测试框架特性标签5.单元测试中的断言Assert6.单元测试中验证预期的异常7.单元测试中针对状态的间接测试8.单元测试在MVC模式中的实现8.单元测试相关参考9.示例源代码下载 志铭-2020年1月23日 11:49:41 1.单元测试…

NuGet是什么?理解与使用(上)

如果你了解python&#xff0c;那么它类似pip。 如果你了解nodejs&#xff0c;那么它类似npm。 如果你了解ruby&#xff0c;那么它类似gem。 对&#xff0c;它就是一个包&#xff08;package&#xff09;管理平台&#xff0c;确切的说是 .net平台的包管理工具&#xff0c;它提…

NuGet是什么?理解与使用(下)

本篇将回答下面几个问题&#xff1a; 如何解读NuGet Gallery上的包信息&#xff1f;如何上传NuGet包到NuGet Gallery&#xff1f;如何安装本地NuGet包&#xff1f;NuGet包的内容文件与目录结构&#xff1f; 如果你没看过上篇那么它在这里&#xff1a; 6号咸鱼&#xff1a;NuG…

C#多线程之旅(1)——介绍和基本概念

阅读目录 一、多线程介绍二、Join 和Sleep三、线程怎样工作四、线程和进程五、线程的使用和误用一、多线程介绍 C#通过多线程支持并行执行的代码。一个线程是一个独立执行的路径&#xff0c;可以同时与其他线程一起运行。一个C#客户端程序(Console,WPF,Winows Forms)开始于一个…

C#多线程之旅(2)——详解线程的开始和创建

阅读目录 代码下载一、线程的创建和开始二、传递数据给一个线程三、命名线程四、前台线程和后台线程五、线程优先级六、异常处理代码下载 Thread_博客园_cnblogs_jackson0714.zip 第一篇~第三篇的代码示例&#xff1a; 源码地址&#xff1a;https://github.com/Jackson0714/T…

C#多线程之旅(3)——线程池

阅读目录 代码下载一、介绍二、通过TPL进入线程池三、不用TPL进入到线程池v博客前言 先交代下背景&#xff0c;写《C#多线程之旅》这个系列文章主要是因为以下几个原因&#xff1a;1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的&#xff0c;如果没有…

C#多线程之旅(4)——APM初探

阅读目录 一、简单的串行执行程序 二、使用委托来实现APM源码地址&#xff1a;https://github.com/Jackson0714/Threads C#多线程之旅(4)——APM初探 v博客前言 先交代下背景&#xff0c;前面几张内容主要是介绍多线程的基本知识&#xff0c;这一章是因为正好接触到了APM&…

C#多线程编程系列(五)- C# ConcurrentBag的实现原理

目录 一、前言二、ConcurrentBag类三、 ConcurrentBag线程安全实现原理 1. ConcurrentBag的私有字段2. 用于数据存储的ThreadLocalList类3. ConcurrentBag实现新增元素4. ConcurrentBag 如何实现迭代器模式四、总结笔者水平有限&#xff0c;如果错误欢迎各位批评指正&#xff…

C#多线程编程系列(五)- 浅析C# Dictionary实现原理

目录 一、前言二、理论知识 1、Hash算法2、Hash桶算法3、解决冲突算法三、Dictionary实现 1. Entry结构体2. 其它关键私有变量3. Dictionary - Add操作4. Dictionary - Find操作5. Dictionary - Remove操作6. Dictionary - Resize操作(扩容)7. Dictionary - 再谈Add操作8. Col…

ASP.NET MVC 入门5、View与ViewData

本系列文章基于ASP.NET MVC Preview5. view在MVC模式中与用户进行最直接的接触&#xff0c;它负责数据的呈现。这里要注意一点就是&#xff0c;view只是负责数据的呈现&#xff0c;所以我们应该要尽量让view中不涉及业务逻辑的处理。 我们来添加一个Blog首页的view。在安装了…

ASP.NET MVC 入门6、TempData

本系列文章基于ASP.NET MVC Preview5. ASP.NET MVC的TempData用于传输一些临时的数据&#xff0c;例如在各个控制器Action间传递临时的数据或者给View传递一些临时的数据&#xff0c;相信大家都看过“在ASP.NET页面间传值的方法有哪几种”这个面试题&#xff0c;在ASP.NET MVC…

ASP.NET MVC 入门11、使用AJAX

本系列文章基于ASP.NET MVC beta.本示例Blog系统同步更新的演示站点&#xff1a;http://4mvcblog.qsh.in/ 在ASP.NET MVC beta发布之前&#xff0c;M$就宣布支持开源的JS框架jQuery&#xff0c;然后ASP.NET MVC beta发布后&#xff0c;你建立一个ASP.NET MVC beta的项目后&…

Nacos 使用

环境准备 64 bit OS&#xff0c;支持 Linux/Unix/Mac/Windows&#xff0c;推荐选用 Linux/Unix/Mac。64 bit JDK 1.8&#xff1b;下载 & 配置。Maven 3.2.x&#xff1b;下载 & 配置。 下载 Nacos 并启动 Nacos server。 启动配置管理 启动了 Nacos server 后&#x…