分析.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.#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(refisProcessing, 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, nullnull);

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就能满足要求,但性能要求比较苛刻的情就需求更具实际场景进行选择哪种线程同步机制。

时间: 2024-08-14 16:26:17

分析.Net里线程同步机制的相关文章

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

原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize

【转】Python线程同步机制: Locks, RLocks, Semaphores, Conditions, Events和Queues

Python线程同步机制: Locks, RLocks, Semaphores, Conditions, Events和Queues | Comments 翻译自Laurent Luce的博客原文名称:Python threads synchronization: Locks, RLocks, Semaphores, Conditions, Events and Queues原文连接:http://www.laurentluce.com/posts/python-threads-synchron

线程同步机制之互斥锁通信机制

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> void *thread_function(void *arg); pthread_mutex_t work_mutex; #define WORK_SIZE 1024 char work_area[WORK_SIZE]; int time_to_exit=0; int main(int argc,

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

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

线程同步机制(二)-- 线程同步辅助类

我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了同步和临界区的概念,并且讨论了多个并发任务共享一个资源时的同步情况.访问共享资源的代码块叫临界区. 我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了一下内容: synchronized关键字 Lock接口及其实现类,如ReentrantLock,ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock 本章我们将学习如

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

一.基础知识. 1:线程同步机制:互斥量,读写锁,条件变量,自旋锁,屏障. 1,互斥量:每个进程访问被互斥量保护的资源时,都需要先对互斥量进行判断. 1)互斥量重要属性:进程共享属性,健壮属性,类型属性. 2)部分系统 不支持 进程共享属性 3)对互斥量重复加锁会导致死锁. 2,读写锁. 1)读写锁有3种状态:读模式加锁,写模式加锁,未加锁. 1-写加锁模式:任何加锁都会被阻塞. 2-读加锁模式:读模式加锁的任何线程都可以得到访问权,同时添加一个读模式锁.但,写模式加锁会被阻塞. 3-在读模式下

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

锁的基础知识 锁的类型 锁从宏观上分类,只分为两种:悲观锁与乐观锁. 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作.Java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败. 悲观

java并发:线程同步机制之ThreadLocal

1.简述ThreadLocal ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程.ThreadLocal是一个线程级别的局部变量,下面是线程局部变量(ThreadLocal variables)的关键点: A.当使用ThreadLocal维护变量时,若多个线程访问ThreadLocal实例,ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应

Python多线程(2)——线程同步机制

本文介绍Python中的线程同步对象,主要涉及 thread 和 threading 模块. threading 模块提供的线程同步原语包括:Lock.RLock.Condition.Event.Semaphore等对象. 1. Lock 1.1 Lock对象的创建 Lock是Python中最底层的同步机制,直接由底层模块 thread 实现,每个lock对象只有两种状态——上锁和未上锁,不同于下文的RLock对象,Lock对象是不可重入的,也没有所属的线程这个概念. 可以通过下面两种方式创建一