C#多线程编程中的锁系统
C#多线程编程中的锁系统(二)
上章主要讲排他锁的直接使用方式。但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了。 这一次我们说说升级锁和原子操作。
目录
1:volatile
2: Interlocked
3:ReaderWriterLockSlim
4:总结
一:volatile
简单来说: volatile关键字是告诉c#编译器和JIT编译器,不对volatile标记的字段做任何的缓存。确保字段读写都是原子操作,最新值。
这不就是锁吗? 其这货它根本不是锁, 它的原子操作是基于CPU本身的,非阻塞的。 因为32位CPU执行赋值指令,数据传输最大宽度4个字节。
所以只要在4个字节以下读写操作的,32位CPU都是原子操作。volatile 它就是利用这个特性来的。
好残酷的事实?不然,微软大法这样是为了提高JIT性能效率,对有些数据进行缓存了(多线程下)。
public volatile Int32 score1 = 1;
//报错
public volatile Int64 score2 = 1;
看上面的例子,我们定义8个字节长度score2就不行了。 因为8个字节,32位CPU就分成2个指令执行了。自然就无法保证原子操作了。
这么细节的,忘了怎么办,那岂不是坑人啊。 于是微软大法直接一棍子打死,限制4个字节以下的类型字段才能用volatile,具体什么、看msdn吧。
那今天我知道了。我编译平台改成64位上,只在64位CPU用volatile int64,行不行? 不行,编译器报错。说了一棍子打死了。。
(^._.^)ノ 好吧,其实可以用IntPtr这个。
volatile多数情况下很有用处的,毕竟锁的性能开销还是很大的。我们可以把当成轻量级的锁,根据具体场景合理使用,能提高不少程序性能。
线程中的Thread.VolatileRead 和Thread.VolatileWrite 就是volatile的复杂版。
二:Interlocked
MSDN 描述:为多个线程共享的变量提供原子操作。主要函数如下:
Interlocked.Increment 原子操作,递增指定变量的值并存储结果。
Interlocked.Decrement 原子操作,递减指定变量的值并存储结果。
Interlocked.Add 原子操作,添加两个整数并用两者的和替换第一个整数
Interlocked.CompareExchange(ref a, b, c); 原子操作,a参数和c参数比较, 相等b替换a,不相等不替换。
基本用法就不多说了。直接来段CLR via C# interlock anything的例子:
{
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 即支持递归模式,线程锁中继续在使用锁。
//do
cacheLock.EnterReadLock();
//do
cacheLock.ExitReadLock();
cacheLock.ExitReadLock();
这种模式极易容易死锁,比如读锁里面使用写锁。
//do
cacheLock.EnterWriteLock();
//do
cacheLock.ExitWriteLock();
cacheLock.ExitReadLock();
下面是直接拿msdn的缓存例子了,加了简单注释。
{
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
};
}
四:总结
多线程实际开发当中,往往测试没问题,一到生产环境,并发高了就容易出问题, 一定注意。
本文参考CLR via C#。
C#多线程编程中的锁系统的更多相关文章
- c#语言-多线程中的锁系统(一)
介绍 平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述. 目录 一:lock.Monitor 1:基础. 2: 作用域. ...
- Java多线程编程(2)--多线程编程中的挑战
一.串行.并发和并行 为了更清楚地解释这三个概念,我们来举一个例子.假设我们有A.B.C三项工作要做,那么我们有以下三种方式来完成这些工作: 第一种方式,先开始做工作A,完成之后再开始做工作B ...
- 关于python多线程编程中join()和setDaemon()的一点儿探究
关于python多线程编程中join()和setDaemon()的用法,这两天我看网上的资料看得头晕脑涨也没看懂,干脆就做一个实验来看看吧. 首先是编写实验的基础代码,创建一个名为MyThread的 ...
- Java多线程编程中Future模式的详解
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- C++ 关于MFC多线程编程中的一些注意事项 及自定义消息的处理
在多线程编程中,最简单的方法,无非就是利用 AfxBeginThread 来创建一个工作线程,看一下这个函数的说明: CWinThread* AFXAPI AfxBeginThread( AFX_T ...
- Java多线程编程中Future模式的详解<转>
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- Qt多线程编程中的对象线程与函数执行线程
近来用Qt编写一段多线程的TcpSocket通信程序,被其中Qt中报的几个warning搞晕了,一会儿是说“Cannot create children for a parent that is in ...
- 【C/C++开发】多线程编程中的join函数
多线程编程中的join函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # coding: utf-8 # 测试多线程中join的 ...
- 多线程中的锁系统(三)-WaitHandle、AutoResetEvent、ManualResetEvent
本章主要介绍下基于内核模式构造的线程同步方式,事件,信号量. 阅读目录: 理论 WaitHandle AutoResetEvent ManualResetEvent 总结 理论 Windows的线程同 ...
随机推荐
- 加入mapstruct后出现 找不到符号 符号: 方法 setXX 的解决方法
加入lombok解决 <build> <plugins> <plugin> <groupId>org.springframework.boot</ ...
- Linux 下配置 iSCSI 客户端
安装客户端软件 Redhat/Centos: yum install -y iscsi-initiator-utils Debian/Ubuntu: apt-get install open-iscs ...
- Python【每日一问】18
问: [基础题]:请解释新式类跟经典类,并说明它们的区别[提高题]:请解释Python垃圾回收机制 答: [基础题]:请解释新式类跟经典类,并说明它们的区别 1.新式类都是继承内置 object 对象 ...
- Qt SDK Issue cstdlib: fatal error: stdlib.h: No such file or directory
*To reproduce the issue, I've tried the following solutions which did not help:* *1) Globally remove ...
- (二)咋使用VUE中的事件修饰符
1,stop修饰符:阻止事件冒泡 首先我们要明确H5的事件是从内向外进行冒泡的,写一个简单的DEMO 当我们点击按钮时,事件从内向外冒泡,依次触发绑定的事件,控制台信息如下 现在我们在click后面添 ...
- 『2019Summer Algorithms』
一个暑假两次集训,感觉学了好多好多的东西,也挖了好多好多的坑,于是就决定写一篇关于算法的总结,用于熟悉新算法,也留下一点对新算法的理解. AC自动机 简单的说就是在\(trie\)树上实现\(KMP\ ...
- Dapper安装与使用
1.VS2015直接使用nuget包搜索Dapper,安装时报错:显示版本不兼容. 于是使用命令安装dapper低版本. 步骤: 打开项目,vs工具---Nuget包管理器--程序包管理器控制台 ...
- HashSet去重
class Program { static void Main(string[] args) { Console.WriteLine( ...
- php 获取当前IP地址
function getIP() { return isset($_SERVER["HTTP_X_FORWARDED_FOR"])?$_SERVER["HTTP_X_FO ...
- C# 调用JS Eval,高效率
/// <summary> /// 动态计算表达式 /// </summary> class JSCaller { /// <summary> /// 动态计算表达 ...