分析.Net里线程同步机制
我 们知道并行编程模型两种:一种是基于消息式的,第二种是基于共享内存式的。 前段时间项目中遇到了第二种 使用多线程开发并行程序共享资源的问题 ,今天以实际案例出发对.net里的共享内存式的线程同步机制做个总结,由于某些类库的应用属于基础,所以本次不对基本使用做出讲解,基本使用 MSDN是最好的教程。
一、volatile关键字
基本介绍: 封装了 Thread.VolatileWrite() 和 Thread.VolatileRead()的实现 ,主要作用是强制刷新高速缓存。
使用场景: 适用于在多核多CPU的机器上 解决变量在内存和高速缓存同步不及时的问题。
案例:参考下文 二、原子操作的 案例 或者 System.Collections.Concurrent命名空间下的 ConcurrentQueue ,ConcurrentDictionary 等并发集合的实现方式。
二、原子操作(Interlock)
基本介绍: 原 子操作是 实现Spinlock,Monitor,ReadWriterLock锁的基础,其实现原理是在计算机总线上标志一个信号来表示资源已经被占用 如果其他指令进行修改则等待本次操作完成后才能进行,因为原子操作是在硬件上实现的 所以速度非常快,大约在50个时钟周期。其实原子操作也可以看做一种锁。
使用场景:性 能要求较高的场合,需要对字段进行快速的同步或者对变量进行原子形式的跟新操作(例如:int b=0; b=b+1 实际分解为多条汇编指令,在多线程情况下 多条汇编指令并行的执行可能导致错误的结果,所以要保证执行 b=b+1 生成的汇编指令是一个原子形式执行 ),例如实现一个并行队列,异步队列等。
案例:一个基于事件触发机制队列的实现
001./// <summary>002./// 表示一个实时处理队列003./// </summary>004.public class ProcessQueue<T>005.{006.#region [成员]007. 008.private ConcurrentQueue<IEnumerable<T>> queue;009. 010.private Action<IEnumerable<T>> PublishHandler;011. 012.//指定处理的线程数013.private int core = Environment.ProcessorCount;014. 015.//正在运行的线程数016.private int runingCore = 0;017. 018.public event Action<Exception> OnException;019. 020.//队列是否正在处理数据021.private int isProcessing=0; 022. 023.//队列是否可用024.private bool enabled = true;025. 026.#endregion027. 028.#region 构造函数029. 030.public ProcessQueue(Action<IEnumerable<T>> handler)031.{032. 033.queue = new ConcurrentQueue<IEnumerable<T>>();034. 035.PublishHandler = handler;036.this.OnException += ProcessException.OnProcessException;037.}038. 039.#endregion040. 041.#region [方法]042. 043./// <summary>044./// 入队045./// </summary>046./// <param name="items">数据集合</param>047.public void Enqueue(IEnumerable<T> items)048.{049.if (items != null)050.{051.queue.Enqueue(items);052.}053. 054.//判断是否队列有线程正在处理 055.if (enabled && Interlocked.CompareExchange(ref isProcessing, 1, 0) == 0)056.{057.if (!queue.IsEmpty)058.{059.ThreadPool.QueueUserWorkItem(ProcessItemLoop);060.}061.else062.{063.Interlocked.Exchange(ref isProcessing, 0);064.}065.}066.}067. 068./// <summary>069./// 开启队列数据处理070./// </summary>071.public void Start()072.{073.Thread process_Thread = new Thread(PorcessItem);074.process_Thread.IsBackground = true;075.process_Thread.Start();076.}077. 078./// <summary>079./// 循环处理数据项080./// </summary>081./// <param name="state"></param>082.private void ProcessItemLoop(object state)083.{084.//表示一个线程递归 当处理完当前数据时 则开起线程处理队列中下一条数据 递归终止条件是队列为空时085.//但是可能会出现 队列有数据但是没有线程去处理的情况 所有一个监视线程监视队列中的数据是否为空,如果为空086.//并且没有线程去处理则开启递归线程087. 088.if (!enabled && queue.IsEmpty)089.{090.Interlocked.Exchange(ref isProcessing, 0);091.return;092.}093. 094.//处理的线程数 是否小于当前CPU核数095.if (Thread.VolatileRead(ref runingCore) <= core * 2*)096.{097.IEnumerable<T> publishFrame;098.//出队以后交给线程池处理099.if (queue.TryDequeue(out publishFrame))100.{101.Interlocked.Increment(ref runingCore);102.try103.{104.PublishHandler(publishFrame);105. 106.if (enabled && !queue.IsEmpty)107.{ 108.ThreadPool.QueueUserWorkItem(ProcessItemLoop);109.}110.else111.{112.Interlocked.Exchange(ref isProcessing, 0);113.}114. 115.}116.catch (Exception ex)117.{118.OnProcessException(ex);119.}120. 121.finally122.{123.Interlocked.Decrement(ref runingCore);124.}125.}126.}127. 128.}129. 130./// <summary>131.///定时处理帧 线程调用函数 132.///主要是监视入队的时候线程 没有来的及处理的情况133./// </summary>134.private void PorcessItem(object state)135.{136.int sleepCount=0;137.int sleepTime = 1000;138.while (enabled)139.{140.//如果队列为空则根据循环的次数确定睡眠的时间141.if (queue.IsEmpty)142.{143.if (sleepCount == 0)144.{145.sleepTime = 1000;146.}147.else if (sleepCount == 3)148.{149.sleepTime = 1000 * 3;150.}151.else if (sleepCount == 5)152.{153.sleepTime = 1000 * 5;154.}155.else if (sleepCount == 8)156.{157.sleepTime = 1000 * 8;158.}159.else if (sleepCount == 10)160.{161.sleepTime = 1000 * 10;162.}163.else164.{165.sleepTime = 1000 * 50;166.}167.sleepCount++;168.Thread.Sleep(sleepTime);169.}170.else171.{172.//判断是否队列有线程正在处理 173.if (enabled && Interlocked.CompareExchange(ref isProcessing, 1, 0) == 0)174.{175.if (!queue.IsEmpty)176.{177.ThreadPool.QueueUserWorkItem(ProcessItemLoop);178.}179.else180.{181.Interlocked.Exchange(ref isProcessing, 0);182.}183.sleepCount = 0;184.sleepTime = 1000;185.}186.}187.}188.}189. 190./// <summary>191./// 停止队列192./// </summary>193.public void Stop()194.{195.this.enabled = false;196. 197.}198. 199./// <summary>200./// 触发异常处理事件201./// </summary>202./// <param name="ex">异常</param>203.private void OnProcessException(Exception ex)204.{205.var tempException = OnException;206.Interlocked.CompareExchange(ref tempException, null, null);207. 208.if (tempException != null)209.{210.OnException(ex);211.}212.}213. 214.#endregion215. 216.}三、自旋锁(Spinlock)
基本介绍: 在原子操作基础上实现的锁,用户态的锁,缺点是线程一直不释放CPU时间片。操作系统进行一次线程用户态到内核态的切换大约需要500个时钟周期,可以根据这个进行参考我们的线程是进行用户等待还是转到内核的等待.。
使用场景:线程等待资源时间较短的情况下使用。
案例: 和最常用的Monitor 使用方法一样 这里就不举例了,在实际场景中应该优先选择使用Monitor,除非是线程等待资源的时间特别的短。
四、监视器(Monitor)
基本介绍: 原子操作基础上实现的锁,开始处于用户态,自旋一段时间进入内核态的等待释放CPU时间片,缺点使用不当容易造成死锁 c#实现的关键字是Lock。
使用场景: 所有需要加锁的场景都可以使用。
案例: 案例太多了,这里就不列出了。
五、读写锁(ReadWriterLock)
原理分析: 原子操作基础上实现的锁,
使用场景:适用于写的次数少,读的频率高的情况。
案例:一个线程安全的缓存实现(.net 4.0 可以使用基础类库中的 ConcurrentDictionary<K,V>) 注意:老版本ReaderWriterLock已经被淘汰,新版的是ReaderWriterLockSlim
01.class CacheManager<K, V>02.{03.#region [成员]04. 05.private ReaderWriterLockSlim readerWriterLockSlim;06. 07.private Dictionary<K, V> containter;08. 09.#endregion10. 11.#region [构造函数]12. 13.public CacheManager()14.{15.this.readerWriterLockSlim = new ReaderWriterLockSlim();16.this.containter = new Dictionary<K, V>();17.}18. 19.#endregion20. 21.#region [方法]22. 23.public void Add(K key, V value)24.{25.readerWriterLockSlim.EnterWriteLock();26. 27.try28.{29.containter.Add(key, value);30.}31. 32.finally33.{34.readerWriterLockSlim.ExitWriteLock();35.}36.}37. 38.public V Get(K key)39.{40. 41.bool result = false;42.V value;43. 44.do45.{46.readerWriterLockSlim.EnterReadLock();47. 48.try49.{50.result = containter.TryGetValue(key, out value);51.}52. 53.finally54.{55.readerWriterLockSlim.ExitWriteLock();56.}57. 58.} while (!result);59. 60.return value;61.}62. 63.#endregion64.}.net中还有其他的线程同步机制:ManualResetEventSlim ,AutoResetEvent ,SemaphoreSlim 这里就逐个进行不介绍 具体在《CLR Via C# 》中解释的非常详细,但在具体的实际开发中我还没有使用到。
最好的线程同步机制是没有同步,这取决于良好的设计,当然有些情况下无法避免使用锁。 在性能要求不高的场合基本的lock就能满足要求,但性能要求比较苛刻的情就需求更具实际场景进行选择哪种线程同步机制。
免费培训课:http://www.jinhusns.com/Products/Curriculum/?type=xcj
源码分享:http://www.jinhusns.com/Products/Download/?type=xcj
分析.Net里线程同步机制的更多相关文章
- windows核心编程 - 线程同步机制
线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...
- 【总结】Java线程同步机制深刻阐述
原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread ...
- ThreadLocal和线程同步机制对比
共同点: ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题. 区别: 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量. 这时该变量是多个线程共享的,使用同 ...
- linux学习笔记之线程同步机制
一.基础知识. 1:线程同步机制:互斥量,读写锁,条件变量,自旋锁,屏障. 1,互斥量:每个进程访问被互斥量保护的资源时,都需要先对互斥量进行判断. 1)互斥量重要属性:进程共享属性,健壮属性,类型属 ...
- Linux程序设计学习笔记----多线程编程线程同步机制之相互排斥量(锁)与读写锁
相互排斥锁通信机制 基本原理 相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下: ...
- Java分享笔记:创建多线程 & 线程同步机制
[1] 创建多线程的两种方式 1.1 通过继承Thread类创建多线程 1.定义Thread类的子类,重写run()方法,在run()方法体中编写子线程要执行的功能. 2.创建子线程的实例对象,相当于 ...
- Java多线程编程(4)--线程同步机制
一.锁 1.锁的概念 线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题.锁 ...
- Java多线程 | 02 | 线程同步机制
同步机制简介 线程同步机制是一套用于协调线程之间的数据访问的机制.该机制可以保障线程安全.Java平台提供的线程同步机制包括: 锁,volatile关键字,final关键字,static关键字,以 ...
- Java并发编程:Java中的锁和线程同步机制
锁的基础知识 锁的类型 锁从宏观上分类,只分为两种:悲观锁与乐观锁. 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新 ...
随机推荐
- Entity Framework中IQueryable, IEnumerable, IList的区别
博客园里有这样的总结.这里姑且先列个题目, 提醒自己记忆.
- 大叔也说Xamarin~Android篇~为HttpClient共享Session,android与api的session共享机制
回到目录 杂谈 在进行android进行开发时,我们的数据一般通过接口来获收,这里指的接口泛指web api,webservice,wcf,web应用程序等:它们做为服务端与数据库进行直接通讯,而AP ...
- 爱上MVC3系列~开发一个站点地图(俗称面包屑)
回到目录 原来早在webform控件时代就有了SiteMap这个东西,而进行MVC时代后,我们也希望有这样一个东西,它为我们提供了不少方便,如很方便的实现页面导航的内容修改,页面导航的样式换肤等. 我 ...
- rabbitmq消息队列——"工作队列"
二."工作队列" 在第一节中我们发送接收消息直接从队列中进行.这节中我们会创建一个工作队列来分发处理多个工作者中的耗时性任务. 工作队列主要是为了避免进行一些必须同步等待的资源密集 ...
- salesforce 零基础开发入门学习(七)PickList的value值获取
之前介绍过PickList类型的声明以及赋值,但是如何取出呢?一个sObject对象可以理解为一条数据.通过sObject直接取恐怕很难做到,因为他只会显示一个值.这时候就要用到Schema命名空间中 ...
- 关于js中的同步和异步
最近看到前端面试问到js中的同步和异步,这个问题该怎么回答? 梳理一下,js对于异步的处理,很多人的第一反应是ajax,这只能说是对了一半. 1.个人觉得,js中,最基础的异步是setTimeout和 ...
- Hadoop学习笔记【分布式文件系统学习笔记】
分布式文件系统介绍 分布式文件系统:Hadoop Distributed File System,简称HDFS. 一.HDFS简介 Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(c ...
- python开启简单webserver
python开启简单webserver linux下面使用 python -m SimpleHTTPServer 8000 windows下面使用上面的命令会报错,Python.Exe: No Mod ...
- 类的继承和多态性-编写Java应用程序,定义Animal类,此类中有动物的属性:名称 name,腿的数量legs,统计动物的数量 count;方法:设置动物腿数量的方法 void setLegs(),获得腿数量的方法 getLegs(),设置动物名称的方法 setKind(),获得动物名称的方法 getKind(),获得动物数量的方法 getCount()。定义Fish类,是Animal类的子类,
编写Java应用程序,定义Animal类,此类中有动物的属性:名称 name,腿的数量legs,统计动物的数量 count;方法:设置动物腿数量的方法 void setLegs(),获得腿数量的方法 ...
- 使用uGUI制作游戏内2D动画
在3D的游戏中制作2D的效果是一个很常见的需求,我在很早前玩过一个叫做艾尔之光的游戏,里面就大量的使用了这个技术.就像下面图片中的伤害数字,这些数字往往还是有一些动画效果在里面的,比如大小的变化,颜色 ...