分析.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中的锁和线程同步机制
锁的基础知识 锁的类型 锁从宏观上分类,只分为两种:悲观锁与乐观锁. 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新 ...
随机推荐
- Visual Studio 2015速递(2)——提升效率和质量(VS2015核心竞争力)
系列文章 Visual Studio 2015速递(1)——C#6.0新特性怎么用 Visual Studio 2015速递(2)——提升效率和质量(VS2015核心竞争力) Visual Studi ...
- Windows 10 下安装 npm 后全局 node_modules 和 npm-cache 文件夹的设置
npm 指 Node Package Manager,是 Node.js 中一个流行的包管理和分发工具.Node.js 在某个版本的 Windows 安装包开始已经加入了 npm,现在可以进入 htt ...
- Linux用户切换
1,查看当前user: whoami 2,普通用户切换到root,会要求输入密码: su 3,root切换到普通用户: su - username
- java source not found
今天的挑战是,为什么与源码明明放在本机,调试的时候却说source not found呢? 这个是因为,我重新建立了debug,然后没有选择对应的项目. 另外一个问题是,如果给底层的jar附上源码呢?
- 前端那点事儿——Tocify自动生成文档目录
今天偶然间看到文档服务器有一个动态目录功能,点击目录能跳转到指定的位置:窗口滑动也能自动更新目录的焦点. 效果 框架 原来使用的是一个开源的jquery-ui控件——tocify.js,它可以遍历页面 ...
- C#Color对象的使用介绍及颜色对照表
原文地址 http://blog.sina.com.cn/s/blog_3e1177090101bzs3.html 今天用到了特转载 NET框架中的颜色基于4种成份,透明度,红,绿和蓝.每一种成份都 ...
- [源码解析]HashMap和HashTable的区别(源码分析解读)
前言: 又是一个大好的周末, 可惜今天起来有点晚, 扒开HashMap和HashTable, 看看他们到底有什么区别吧. 先来一段比较拗口的定义: Hashtable 的实例有两个参数影响其性能:初始 ...
- 关于python的最大递归层数详解
在阅读http://www.cnblogs.com/skabyy/p/3451780.html这篇文章的时候,实验yield的流式迭代素数的时候发现有个问题,故详细记录下来. 首先来看看python默 ...
- Atitit vod click event design flow 视频点播系统点击事件文档
Atitit vod click event design flow 视频点播系统点击事件文档 重构规划1 Click cate1 Click mov4 重构规划 事件注册,与事件分发管理器分开 ...
- NPM安装之后CMD中不能使用
NPM安装之后CMD中不能使用 这个情况就是path环境变量没有添加NPM 添加环境变量并重启CMD C:\Users\Mark\AppData\Roaming\npm\ 看看这个文件夹就知道为什么要 ...