分析.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(
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里线程同步机制的更多相关文章
- 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中的锁和线程同步机制
锁的基础知识 锁的类型 锁从宏观上分类,只分为两种:悲观锁与乐观锁. 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新 ...
随机推荐
- 机械革命 USB装系统各种坑
买了个号称超强性价比的游戏本- 机械革命, i7+ssd+hd+4G RAM+ GTX850M, 很直接, 直接出厂就一个DOS系统,回来要自己装机. 好吧, 先下了个大白菜软件,用来刻录ISO系统到 ...
- java系统性能分析
netstat -ano | findstr 31900 注意最后是pid 堆栈的作用: 线程死锁分析 辅助CPU过高分析 线程资源不足分析 性能瓶颈分析 关键线程异常退出 Windows:在运行ja ...
- http流请求时,被请求站点HttpContext.Current为null?
我负责运维一个短信接口站点sms.调用上游短信供应商下发短信后,他们会给我们推送发送报告.报告是类似DELIVRD.DI:9432这样的码.为了方便识别,系统里有一个报告码与其描述的关系,一开始是写死 ...
- 爱上MVC~为CheckBoxFor和RadioButtonFor加个扩展方法吧(希望MVC5把这方法收纳——呵呵)
回到目录 说在前 我都是喜欢把问题复杂化,还有总是喜欢把问题简单化,偷懒化,这也需就是一个程序员的追求吧,呵呵. 我不太喜欢重复的东西,当你看到页面上有一个以上相同的代码时,那可以说,你的代码有重构的 ...
- Atitit 项目培训与学校的一些思路总结
Atitit 项目培训与学校的一些思路总结 1.1. Overview implet review OIR学习大法1 1.2. "录取流程,对报名者唯一的要求是学习该项目所必须的先修知识和 ...
- SSH框架详解
1.什么是ssh? SSH对应 struts spring hibernate struts 采用MVC模式,主要是作用于用户交互 spring 采用IOC和AOP~作用比较抽象,是用于项目的松耦合 ...
- iOS开发——高级技术精选OC篇&Runtime之字典转模型实战
Runtime之字典转模型实战 如果您还不知道什么是runtime,那么请先看看这几篇文章: http://www.cnblogs.com/iCocos/p/4734687.html http://w ...
- 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序
学习ASP.NET MVC系列: 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)——我的第一个ASP.NET MVC 控制器 学习ASP ...
- Ubuntu14中supervisor的安装及配置
supervisor是一款很好用的进程管理工具,其命令也很简单,其安装过程如下: Ubuntu14: 首先保证本地的Python环境是OK的,并且已经安装supervisor包,如果没有安装可以用ea ...
- 【WP 8.1开发】电子罗盘
罗盘,估计也不用我过多介绍,学过初中物理的都知道,不管是指南针,还是指北针,其本质就是用来辨别方向的. 操作电子罗盘伟感器也不复杂,主要就是两个角度: 1.当前方向与磁北的夹角: 2.当前方向与地北的 ...