c# .NET 高级编程 高并发必备技巧 - 锁
锁 最为常见的应用就是 高并发的情况下,库存的控制。本次只做简单的单机锁介绍。
直接看代码:
每请求一次库存-1. 假如库存1000,在1000个人请求之后,库存将变为0。
public int Reduce0(){int r = 0;string key = "stock";string stock = Rds.cli.Get(key);int.TryParse(stock, out r);if (r > 0){r--;Rds.cli.Set(key, r);}else{throw new Exception("库存用尽!");}return r;}
本次测试使用Jmeter进行测试。先初始化库存为1000。
Jmeter 设置如下,一个线程请求1000次,再去查看库存刚好为0,没有任何问题:
调整一下测试参数,5个人同时请求,各请求200次。再去查看库存
发现请求后,还有279的库存。明明请求了1000次。但是还有279的库存,明显不对。
造成次问题的原因很简单,就是在库存还没完全减的情况下,有另外一个、或多个线程同时发出了请求,而库存只减少了1
只要还有库存,就可以继续请求,到了库存完全为0的时候,已经超过1000个人进行了请求。与实际库存不符合。
为了解决这个问题。我们简单调整一下代码:
private static object lck = new object();[HttpGet]public int Reduce1(){lock(lck){int r = 0;string key = "stock";string stock = Rds.cli.Get(key);r = int.Parse(stock);if (r > 0){r--;Rds.cli.Set(key, r);}else{throw new Exception("库存用尽!");}return r;}}
声明一个静态变量,然后再方法体内 使用lock。调整代码后,再次进行测试:
发现 请求1000次后,库存为0。调整测试参数 100人*10次。测试结果依然为0。
到此为止,问题解决。
但是,实际应用场景中,高并发的应用,都会多机分布式部署。分布式部署要怎么解决?大家思考一下。
上一篇文章简单的介绍了单机的情况下如何进行加锁,防止高并发带来的问题。
然而现实中,一般会高并发的应用,很少会单机部署。当用户量达到一定的程度,分布式、集群部署是必然的选择。在分布式部署的情况下,之前的单机锁还会有效吗?代码还是之前的代码:
private static object lck = new object();/// <summary>/// 单机锁/// </summary>/// <returns></returns>/// <exception cref="Exception"></exception>[HttpGet]public int Reduce1(){lock(lck){int r = 0;string key = "stock";string stock = Rds.cli.Get(key);r = int.Parse(stock);if (r > 0){r--;Rds.cli.Set(key, r);}else{throw new Exception("库存用尽!");}return r;}}
今天再来测试一下,首先在本机模拟分布式的部署。
api 部署3个,分别对应的端口 1020、1021、1022。使用nginx进行负载均衡转发,Nginx简单配置信息如下:
Jmeter请求的接口是nginx的8000,请求线程和上一次一样100*10
1000次请求后,再去查库存,发现库存并不为0。所以单机锁,在分布式的情况下,根本没起作用。
所以在分布式的情况下,必须要借助第三方的中间件。Redis是其中比较常见的解决方案,以下是简单的实现代码:
/// <summary>/// 分布式锁/// </summary>/// <returns></returns>/// <exception cref="Exception"></exception>[HttpGet]public int Reduce2(){bool Lck1 =false;int r = 0;string identity=Guid.NewGuid().ToString(); //设置识别,避免错误释放锁。int OverTime = 10; //根据实际业务场景设置 超时时间,避免出现死锁try{Lck1 = Rds.cli.SetNx("lock", identity, OverTime);while (!Lck1){Lck1 = Rds.cli.SetNx("lock", identity, OverTime);}string key = "stock";string stock = Rds.cli.Get(key);r = int.Parse(stock);if (r > 0){r--;Rds.cli.Set(key, r);}else{throw new Exception("库存用尽!");}}catch (Exception ex){throw;}finally{string id = Rds.cli.Get("lock");if(id==identity){Rds.cli.Del("lock");}}return r;}
再次通过Jmeter 请求nginx 的端口。经过改造后的代码方法,在1000次请求后,库存已经为0。说明此次的分布式锁是有效的。
需要注意的是,要避免死锁,所以加锁的时候,要根据业务场景 设置 过期时间。
为了避免释放错误,加锁的时候也要加上身份认证。
好了此次关于锁的分享完毕。
转自:https://www.cnblogs.com/pzscit/p/17644157.html