第十四节: 介绍四大并发集合类并结合单例模式下的队列来说明线程安全和非安全的场景及补充性能调优问题。...
一. 四大并发集合类
背景:我们目前使用的所有集合都是线程不安全的 。
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);}
View Code
②. 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 }
View Code
③. 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 }
View Code
3. 在实际项目中,如果使用队列来实现一个业务,该队列需要是全局的,这个时候就需要使用单例(ps:单例是不允许被实例化的,可以通过单例类中的属性或者方法的形式来获取这个类),同时,队列的入队和出队操作,如果使用Queue队列,需要配合lock锁,来解决多线程下资源的竞用问题。
经典案例:开启100个线程对其进行入队操作,然后主线程输入队列的个数,并且将队列中的内容输出.
结果:队列的个数为100,输出内容1-100依次输出。
1 /// <summary> 2 /// 单例类 3 /// </summary> 4 public class QueueUtils 5 { 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. 运用设计模式,使代码简洁、易于扩展。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
转载于:https://www.cnblogs.com/yaopengfei/p/8322016.html
第十四节: 介绍四大并发集合类并结合单例模式下的队列来说明线程安全和非安全的场景及补充性能调优问题。...相关推荐
- 第十四节: 介绍四大并发集合类并结合单例模式下的队列来说明线程安全和非安全的场景及补充性能调优问题。
一. 四大并发集合类 背景:我们目前使用的所有集合都是线程不安全的 . A. ConcurrentBag:就是利用线程槽来分摊Bag中的所有数据,链表的头插法,0代表移除最后一个插入的值. (等价于同 ...
- 介绍四大并发集合类并结合单例模式下的队列来说明线程安全和非安全的场景及补充性能调优问题。
一. 四大并发集合类 背景:我们目前使用的所有集合都是线程不安全的 . A. ConcurrentBag:就是利用线程槽来分摊Bag中的所有数据,链表的头插法,0代表移除最后一个插入的值. (等价于同 ...
- 第三百二十四节,web爬虫,scrapy模块介绍与使用
第三百二十四节,web爬虫,scrapy模块介绍与使用 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中. 其最初是为 ...
- Python编程基础:第五十四节 排序Sort
第五十四节 排序Sort 前言 实践 前言 我们常需要对列表.元组中的元素进行排序,例如按照字母表排列学生的名称.这里就需要用到列表的sort()方法,以及sorted()函数. 实践 我们先来构建一 ...
- Python编程基础:第四十四节 方法重写Method Overriding
第四十四节 方法重写Method Overriding 前言 实践 前言 我们前面说了,子类继承于父类,可以调用父类的所有属性和方法.那么如果我们想在继承的过程中重新书写父类的某些方法,此时就用到了方 ...
- Python编程基础:第三十四节 文件移动Move a File
第三十四节 文件移动Move a File 前言 实践 前言 当我们需要将一个文件/文件夹移动到另一个指定路径时,就需要用到shutil.move()函数,该函数需要指定两个参数shutil.move ...
- Python编程基础:第二十四节 作用域Scope
第二十四节 作用域Scope 前言 实践 前言 在Python中每一个变量都有其自己的生命周期,我们一般将变量分为全局变量与局部变量,全局变量是指在整个代码内部都可以访问到的变量,局部变量是指只有在函 ...
- Python编程基础:第十四节 列表Lists
第十四节 列表Lists 前言 实践 前言 列表是一种非常常用的数据结构.我们可以用它来存储各种类型的数据. 实践 我们先来创建一个名为food的列表,里面存储了一系列我喜欢的食物名称: food = ...
- 大白话5分钟带你走进人工智能-第十四节过拟合解决手段L1和L2正则
第十四节过拟合解决手段L1和L2正则 第十三节中,我们讲解了过拟合的情 ...
最新文章
- 华为鸿蒙运行视频,某游戏在华为鸿蒙运行,被识别成使用安卓模拟器
- 不要怂,就是GAN (生成式对抗网络) (六):Wasserstein GAN(WGAN) TensorFlow 代码
- Java 收集的代码 transient
- 接口中不能有方法体吗-------不是
- 汇编中数据处理的基本问题
- 树状数组(单点+区间的所有操作)
- Python 3.9.1 安装教程
- HTTPS和HTTPS证书
- mac按文件名查找文件_如何在Mac上查找和删除大文件
- AD16原理图.schdot中批量修改标签中的文本字体、大小、颜色
- java gc回收机制种类_JAVA的垃圾回收机制(GC)
- linux配置nginx命令行,Linux - 加上sudo后,nginx找不到命令
- mysql master or master copy
- SPH(光滑粒子流体动力学)流体模拟实现二:SPH算法(2)-粒子受力分析
- 苹果se2_苹果12mini登场后SE2彻底沦为智商检测机?网友:太高明
- C#_基础_部分类partial(十八)
- Javascript基础知识笔记二
- CF984D XOR-pyramid
- PS小技巧----证件照换底色
- 字节跳动Java金三银四解析:阿里巴巴技术专家之作
热门文章
- 提交响应后无法调用sendredirect_微服务的那些事(三),微服务的远程调用方式。RPC和HTTP...
- 关于对锐捷光交换机的使用
- html5复选框样式,11种炫酷CSS3复选框checkbox样式美化效果
- java web oracle 分页_Oracle分页的两种方式
- python3装饰器详解_Python装饰器详解
- FPGA之道(69)提高设计的综合性能(一)提高设计的鲁棒性
- FPGA之道(57)状态机的实现方式
- 【 FPGA/IC 】addsub 的实现
- SAP Query达到select * where 的效果 2011-04-29
- robot.txt 搜索引擎 蜘蛛爬虫 搜索规则