我 们知道并行编程模型两种:一种是基于消息式的,第二种是基于共享内存式的。 前段时间项目中遇到了第二种 使用多线程开发并行程序共享资源的问题 ,今天以实际案例出发对.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.#endregion
027. 
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.#endregion
040. 
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.else
062.{
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.try
103.{
104.PublishHandler(publishFrame);
105. 
106.if (enabled && !queue.IsEmpty)
107.{   
108.ThreadPool.QueueUserWorkItem(ProcessItemLoop);
109.}
110.else
111.{
112.Interlocked.Exchange(ref isProcessing, 0);
113.}
114. 
115.}
116.catch (Exception ex)
117.{
118.OnProcessException(ex);
119.}
120. 
121.finally
122.{
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.else
164.{
165.sleepTime = 1000 * 50;
166.}
167.sleepCount++;
168.Thread.Sleep(sleepTime);
169.}
170.else
171.{
172.//判断是否队列有线程正在处理
173.if (enabled && Interlocked.CompareExchange(ref isProcessing, 1, 0) == 0)
174.{
175.if (!queue.IsEmpty)
176.{
177.ThreadPool.QueueUserWorkItem(ProcessItemLoop);
178.}
179.else
180.{
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.#endregion
215. 
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.#endregion
10. 
11.#region [构造函数]
12. 
13.public CacheManager()
14.{
15.this.readerWriterLockSlim = new ReaderWriterLockSlim();
16.this.containter = new Dictionary<K, V>();
17.}
18. 
19.#endregion
20. 
21.#region [方法]
22. 
23.public void Add(K key, V value)
24.{
25.readerWriterLockSlim.EnterWriteLock();
26. 
27.try
28.{
29.containter.Add(key, value);
30.}
31. 
32.finally
33.{
34.readerWriterLockSlim.ExitWriteLock();
35.}
36.}
37. 
38.public V Get(K key)
39.{
40. 
41.bool result = false;
42.V value;
43. 
44.do
45.{
46.readerWriterLockSlim.EnterReadLock();
47. 
48.try
49.{
50.result = containter.TryGetValue(key, out value);
51.}
52. 
53.finally
54.{
55.readerWriterLockSlim.ExitWriteLock();
56.}
57. 
58.} while (!result);
59. 
60.return value;
61.}
62. 
63.#endregion
64.}

      .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里线程同步机制的更多相关文章

  1. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  2. 【总结】Java线程同步机制深刻阐述

    原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread ...

  3. ThreadLocal和线程同步机制对比

    共同点: ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题. 区别: 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量. 这时该变量是多个线程共享的,使用同 ...

  4. linux学习笔记之线程同步机制

    一.基础知识. 1:线程同步机制:互斥量,读写锁,条件变量,自旋锁,屏障. 1,互斥量:每个进程访问被互斥量保护的资源时,都需要先对互斥量进行判断. 1)互斥量重要属性:进程共享属性,健壮属性,类型属 ...

  5. Linux程序设计学习笔记----多线程编程线程同步机制之相互排斥量(锁)与读写锁

    相互排斥锁通信机制 基本原理 相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下: ...

  6. Java分享笔记:创建多线程 & 线程同步机制

    [1] 创建多线程的两种方式 1.1 通过继承Thread类创建多线程 1.定义Thread类的子类,重写run()方法,在run()方法体中编写子线程要执行的功能. 2.创建子线程的实例对象,相当于 ...

  7. Java多线程编程(4)--线程同步机制

    一.锁 1.锁的概念   线程安全问题的产生是因为多个线程并发访问共享数据造成的,如果能将多个线程对共享数据的并发访问改为串行访问,即一个共享数据同一时刻只能被一个线程访问,就可以避免线程安全问题.锁 ...

  8. Java多线程 | 02 | 线程同步机制

    同步机制简介 ​ 线程同步机制是一套用于协调线程之间的数据访问的机制.该机制可以保障线程安全.Java平台提供的线程同步机制包括: 锁,volatile关键字,final关键字,static关键字,以 ...

  9. Java并发编程:Java中的锁和线程同步机制

    锁的基础知识 锁的类型 锁从宏观上分类,只分为两种:悲观锁与乐观锁. 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新 ...

随机推荐

  1. HTML5打造的炫酷本地音乐播放器-喵喵Player

    将之前捣腾的音乐频谱效果加上一个播放列表就成了现在的喵喵播放器(Meow meow Player,额知道这名字很二很装萌~),全HTML5打造的网页程序,可本地运行也可以挂服务器上用. 在线Demo及 ...

  2. C#将dll打包到程序中

    最近比较懒,加上内容也不多就懒得排版了,字放大了,看起来应该方便一点 直接进入主题 先来看一个栗子,假设现在有一个第三方dll namespace TestLibrary1 { public clas ...

  3. 翻译:AKKA笔记 - 介绍Actors

    任何以前做过多线程的人都不会否认管理多线程程序是困难并且痛苦的. 我说管理是因为它开始很容易而且当你看到性能提升时会很兴奋.但是,当你看到你没法从子线程的错误中恢复 或者 这些僵尸bug很难重现 或者 ...

  4. Redis服务器的启动过程分析

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/127.html?1455808771 本文将通过分析代码来介绍Redis的 ...

  5. NFC:Arduino、Android与PhoneGap近场通信

    NFC:Arduino.Android与PhoneGap近场通信(第一本全面讲解NFC应用开发的技术著作移动智能设备近距离通信编程实战入门) [美]Tom Igoe(汤姆.伊戈),Don Colema ...

  6. 关于json序列化和反序列的问题,没事写个案例,希望能帮到那些需要帮忙的朋友!

    现在关于json的读写问题,有许许多多的解决方法,因人而异,根据实际问题去选择自己想要的最容易方法.我觉得自带的Newtonsoft.Json是个不错的选择,随便写两个例子吧! 一:关于简单的json ...

  7. Nokia Lumia通过电脑来升级Windows Phone 8.1

    现在基本上所有lumia都推送了WP 8.1了,不过,有些朋友说在更新过程中常常断线,导致要重新下载.不知道是不是我的人品比较正能量,我从预览版升级,到正式版升级,都没有出现断网现象,每次都能顺利更新 ...

  8. java集合框架之List

    一.List: 1.  特有的常见方法:(有个共性特点就是都可以操作角标) (1).添加 void add(int Index , E element):在list的指定位置插入元素 void add ...

  9. 再谈collections模块defaultdict()和namedtuple()

    defaultdict()和namedtuple()是collections模块里面2个很实用的扩展类型.一个继承自dict系统内置类型,一个继承自tuple系统内置类型.在扩展的同时都添加了额外的很 ...

  10. Docker之Linux UnionFS

    UnionFS UnionFS是一种为Linux,FreeBSD和NetBSD操作系统设计的把其他文件系统联合到一个联合挂载点的文件系统服务.它使用branch把不同文件系统的文件和目录"透 ...