多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim
上章主要讲排他锁的直接使用方式。但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了,本篇主要介绍下升级锁和原子操作。
阅读目录
volatile
简单来说volatile关键字是告诉c#编译器和JIT编译器,不对volatile标记的字段做任何的缓存。确保字段读写都是原子操作,最新值。
从功能上看起到锁的作用,但它不是锁, 它的原子操作是基于CPU本身的,非阻塞的。 因为32位CPU执行赋值指令,数据传输最大宽度4个字节。
所以只要在4个字节以下读写操作的,32位CPU都是原子操作,volatile 是利用这个特性来保证其原子操作的。
这样的目的是为了提高JIT性能效率,对有些数据进行缓存了(多线程下)。
//正确
public volatile Int32 score1 = ;
//报错
public volatile Int64 score2 = ;
如上,我们尝试定义了8个字节长度score2,会抛出异常。 因为8个字节32位CPU就分成2个指令执行了,所以就无法保证其原子操作了。
如果把编译平台改成64位,同样不可以使用,C#限制4个字节以下的类型字段才能用volatile。

还一种方法是使用特定平台的整数类型IntPtr,这样volatile即可作用到64位上了。
volatile多数情况下很有用处的,毕竟锁的性能开销还是很大的。可以把当成轻量级的锁,根据具体场景合理使用,能提高不少程序性能。
线程中的Thread.VolatileRead 和Thread.VolatileWrite 是volatile以前的版本。
Interlocked
MSDN 描述:为多个线程共享的变量提供原子操作。主要函数如下:
Interlocked.Increment 原子操作,递增指定变量的值并存储结果。
Interlocked.Decrement 原子操作,递减指定变量的值并存储结果。
Interlocked.Add 原子操作,添加两个整数并用两者的和替换第一个整数
Interlocked.CompareExchange(ref a, b, c); 原子操作,a参数和c参数比较, 相等b替换a,不相等不替换。
下面是个interlock anything的例子:
public static int Maximum(ref int target, int value)
{
int currentVal = target, startVal, desiredVal; //记录前后值
do
{
startVal = currentVal; //记录循环迭代的初始值。
desiredVal = Math.Max(startVal, value); //基于startVal和value计算期望值desiredVal //高并发下,线程被抢占情况下,target值会发生改变。 //target startVal相等说明没改变。desiredVal 直接替换。
currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal); } while (startVal != currentVal); //不相等说明,target值已经被其他线程改动。自旋继续。
return desiredVal;
}
ReaderWriterLockSlim
假如有份缓存数据A,如果每次都不管任何操作lock一下,那么我的这份缓存A就永远只能单线程读写了, 这在Web高并发下是不能忍受的。
那有没有一种办法我只在写入时进入独占锁呢,读操作时不限制线程数量呢?答案就是我们的ReaderWriterLockSlim主角,读写锁。
ReaderWriterLockSlim 其中一种锁EnterUpgradeableReadLock最关键 即可升级锁。
它允许你先进入读锁,发现缓存A不一样了, 再进入写锁,写入后退回读锁模式。
ps: 这里注意下net 3.5之前有个ReaderWriterLock 性能较差。推荐使用升级版的 ReaderWriterLockSlim 。
//实例一个读写锁
ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
上面实例一个读写锁,这里注意的是构造函数的枚举。
LockRecursionPolicy.NoRecursion 不支持,发现递归会抛异常。
LockRecursionPolicy.SupportsRecursion 即支持递归模式,线程锁中继续在使用锁。
cacheLock.EnterReadLock();
//do
cacheLock.EnterReadLock();
//do
cacheLock.ExitReadLock();
cacheLock.ExitReadLock();
这种模式极易容易死锁,比如读锁里面使用写锁。
cacheLock.EnterReadLock();
//do
cacheLock.EnterWriteLock();
//do
cacheLock.ExitWriteLock();
cacheLock.ExitReadLock();
下面是msdn的缓存例子了,加了注释。
public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>(); public string Read(int key)
{
//进入读锁,允许其他所有的读线程,写入线程被阻塞。
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
} public void Add(int key, string value)
{
//进入写锁,其他所有访问操作的线程都被阻塞。即写独占锁。
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
} public bool AddWithTimeout(int key, string value, int timeout)
{
//超时设置,如果在超时时间内,其他写锁还不释放,就放弃操作。
if (cacheLock.TryEnterWriteLock(timeout))
{
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return true;
}
else
{
return false;
}
} public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
//进入升级锁。 同时只能有一个可升级锁线程。写锁,升级锁都被阻塞,但允许其他读取数据的线程。
cacheLock.EnterUpgradeableReadLock();
try
{
string result = null;
if (innerCache.TryGetValue(key, out result))
{
if (result == value)
{
return AddOrUpdateStatus.Unchanged;
}
else
{
//升级成写锁,其他所有线程都被阻塞。
cacheLock.EnterWriteLock();
try
{
innerCache[key] = value;
}
finally
{
//退出写锁,允许其他读线程。
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;
}
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Added;
}
}
finally
{
//退出升级锁。
cacheLock.ExitUpgradeableReadLock();
}
} public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
};
}
多线程实际开发当中一直是个难点,特别是并发量比较高的情况下,这需要使用时尤为注意。
多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim的更多相关文章
- c#语言-多线程中的锁系统(一)
介绍 平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述. 目录 一:lock.Monitor 1:基础. 2: 作用域. ...
- 多线程中的锁系统(三)-WaitHandle、AutoResetEvent、ManualResetEvent
本章主要介绍下基于内核模式构造的线程同步方式,事件,信号量. 阅读目录: 理论 WaitHandle AutoResetEvent ManualResetEvent 总结 理论 Windows的线程同 ...
- C#多线程编程中的锁系统
C#多线程编程中的锁系统(二) 上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了. 这一次我们说说升级锁和原子操作. 目录 1:volatile 2: Inter ...
- java 多线程中的锁的类别及使用
目前在Java中存在两种锁机制: synchronized Lock Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea. 数据同步需要依赖锁,那锁的同步又依赖谁? ...
- 深入理解Java中的锁(二)
locks包结构层次 Lock 接口 方法签名 描述 void lock(); 获取锁(不死不休) boolean tryLock(); 获取锁(浅尝辄止) boolean tryLock(long ...
- java 多线程研究:锁的概念
java多线程:锁 java的多线程中的锁是干嘛的呢?在网上找了很多博客,大都是很专业的语言,让我一时间摸不着头脑.下面分三个部分来总结多线程中的锁的概念. 一,基础概念: 多线程在运行的时候可能会遇 ...
- 【多线程】不懂什么是 Java 中的锁?看看这篇你就明白了!
本文来源:Java建设者 原文地址:https://mp.weixin.qq.com/s/GU42BjM5jY2CEMVD_PAZBQ Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种 ...
- Java多线程并发08——锁在Java中的应用
前两篇文章中,为各位带来了,锁的类型及锁在Java中的实现.接下来本文将为各位带来锁在Java中的应用相关知识.关注我的公众号「Java面典」了解更多 Java 相关知识点. 锁在Java中主要应用还 ...
- Java 多线程:锁(二)
Java 多线程:锁(二) 作者:Grey 原文地址: 博客园:Java 多线程:锁(二) CSDN:Java 多线程:锁(二) AtomicLong VS LongAddr VS Synchroni ...
随机推荐
- C#运算符号
double x=5.1e3;// 5.1乘以10 的3次方. x就是 5100 //注 : 5.1e+3=5.1e3=5.1e03=5.1e+03 double y=5.1e-3;// 5.1乘以 ...
- ExtJS 数据模型
自定义数据模型 数据模型类其实就是一个继承自Ext.data.Model 的类. Ext.define('MyApp.User', { extend: 'Ext.data.Model', fields ...
- sqlmap 帮助信息
Usage: sqlmap.py [options] 选项: -h, --help 显示基本的帮助信息并退出 -hh 显示高级的帮助信息并退出 --version 显示程序版本号并退出 -v VERB ...
- Mesos高可用解决方案剖析
本文作者王勇桥,80后的IT攻城狮,供职于IBM多年,Mesos和Swarm社区的贡献者.本文是他根据自己对Mesos的高可用(High-Availability)设计方案的了解以及在Mesos社区贡 ...
- NoSQL 数据库分类
类型 部分代表 特点 列存储 Hbase Cassandra Hypertable 顾名思义,是按列存储数据的.最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有 ...
- [R语言]R语言计算unix timestamp的坑
R+mongo的组合真是各种坑等着踩 由于mongo中的时间戳普遍使用的是unix timestamp的格式,因此需要对每天的数据进行计算的时候,很容易就想到对timestamp + gap对方式来实 ...
- bzoj3123: [Sdoi2013]森林
题面传送门 复出的第一道题.. md就遇到坑了.. 简单来说就是可持久化线段树+启发式合并啊.. 感觉启发式合并好神奇好想学 每一次建边就暴力合并,每一个节点维护从根到它的权值线段树 按照题面的话最省 ...
- .net工具
程序名称 作者 说明 文件结构与元数据查看看 AssemblyView1.0 可以查看.net平台下exe,dll源代码的类结构,比如变量,属性,函数,事件的定义. Anakrino 源代码开 ...
- iOS开发资源(持续更新)
vm10虚拟机安装Mac OS X10.10教程 马上着手开发 iOS 应用程序 (Start Developing iOS Apps Today) Xcode使用教程详细讲解 (上) Xcode使用 ...
- iOS 面试总结 二
1.用三种方法生成内容为数字 1,2 ,3 的可变数组.(使用Objective-C,尽量一行代码实现) //方法一 NSMutableArray *arr1 = [[NSMutableArray a ...