上章主要讲排他锁的直接使用方式。但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了,本篇主要介绍下升级锁和原子操作。

阅读目录

  1. volatile
  2. Interlocked
  3. 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的更多相关文章

  1. c#语言-多线程中的锁系统(一)

    介绍 平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述.   目录 一:lock.Monitor        1:基础.        2: 作用域.       ...

  2. 多线程中的锁系统(三)-WaitHandle、AutoResetEvent、ManualResetEvent

    本章主要介绍下基于内核模式构造的线程同步方式,事件,信号量. 阅读目录: 理论 WaitHandle AutoResetEvent ManualResetEvent 总结 理论 Windows的线程同 ...

  3. C#多线程编程中的锁系统

    C#多线程编程中的锁系统(二) 上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了. 这一次我们说说升级锁和原子操作. 目录 1:volatile 2:  Inter ...

  4. java 多线程中的锁的类别及使用

    目前在Java中存在两种锁机制: synchronized Lock Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea. 数据同步需要依赖锁,那锁的同步又依赖谁? ...

  5. 深入理解Java中的锁(二)

    locks包结构层次 Lock 接口 方法签名 描述 void lock(); 获取锁(不死不休) boolean tryLock(); 获取锁(浅尝辄止) boolean tryLock(long ...

  6. java 多线程研究:锁的概念

    java多线程:锁 java的多线程中的锁是干嘛的呢?在网上找了很多博客,大都是很专业的语言,让我一时间摸不着头脑.下面分三个部分来总结多线程中的锁的概念. 一,基础概念: 多线程在运行的时候可能会遇 ...

  7. 【多线程】不懂什么是 Java 中的锁?看看这篇你就明白了!

    本文来源:Java建设者 原文地址:https://mp.weixin.qq.com/s/GU42BjM5jY2CEMVD_PAZBQ Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种 ...

  8. Java多线程并发08——锁在Java中的应用

    前两篇文章中,为各位带来了,锁的类型及锁在Java中的实现.接下来本文将为各位带来锁在Java中的应用相关知识.关注我的公众号「Java面典」了解更多 Java 相关知识点. 锁在Java中主要应用还 ...

  9. Java 多线程:锁(二)

    Java 多线程:锁(二) 作者:Grey 原文地址: 博客园:Java 多线程:锁(二) CSDN:Java 多线程:锁(二) AtomicLong VS LongAddr VS Synchroni ...

随机推荐

  1. F#之旅9 - 正则表达式

    今天,cozy群有个群友发了条正则,问正则匹配相关的问题.虽然他的问题用html selector去处理可能更好,但是我也再一次发现:我忘了正则怎么写的了! 忘掉正则是有原因的,这篇文章会简单记录下F ...

  2. postgresql数据库

    新建一个虚拟机,在本地跑程序,连虚拟机数据库报错: no pg_hba.conf entry for host "192.168.1.4" Google下,发现是要修改postgr ...

  3. Shell_2 语句

    1 if else fi if 语句通过关系运算符判断表达式的真假来决定执行哪个分支.Shell 有三种 if ... else 语句: if ... fi 语句: if ... else ... f ...

  4. sqlite like 通配符 ,匹配区分大小写(默认不区分大小写)

    在查询前先执行这个语句 , 1 时区分大小写,0时不区分 PRAGMA case_sensitive_like =0; select prod_name,PROD_PRICEfrom products ...

  5. Unity5中WebGL平台封装的一些技巧

    最近在接触unity的WebGL平台,其实这个平台作为Web Player的替代品,已经能满足大部分的开发需求,而且不需要额外的插件支持,确实方便了不少,但开发中依旧遇到了不少问题,在这里记录和共享一 ...

  6. 通过url传参

    简介: 通过url传单个和多个参数 你得配Route; 你得使用ui-sref传参数; 在你相关Route的Controller里面Inject $stateParams,然后取参数就OK. 实例: ...

  7. 数据库访问性能优化 Oracle

    特别说明: 1.  本文只是面对数据库应用开发的程序员,不适合专业DBA,DBA在数据库性能优化方面需要了解更多的知识: 2.  本文许多示例及概念是基于Oracle数据库描述,对于其它关系型数据库也 ...

  8. HTML5 学习笔记(一)——HTML5概要与新增标签

    目录 一.HTML5概要 1.1.为什么需要HTML5 1.2.什么是HTML5 1.3.HTML5现状及浏览器支持 1.4.HTML5特性 1.5.HTML5优点与缺点 1.5.1.优点 1.5.2 ...

  9. pair的使用

    #include<iostream> #include<cmath> #include<cstdio> #include<algorithm> #inc ...

  10. WindowManager massge和handler

    在一个可移动浮动按钮的demo源码学习中,有一些WindowManager的使用,在此做下总结. 1.翻译过来就是窗口管理,是和应用框架层的窗口管理器交互的接口,通过 mWindowManager = ...