StackExchange Redis 锁 与 代码级锁

直接上代码看现象来进行直观描述
示例场景描述:
redis中存储一个string类型数据,key=key,value=0,;
两个线程,每个线程循环100次,每次对key值进行+1操作;
期望结果:两个线程共执行了200次加操作,输出结果200;
这里为了展示锁的效果,我们故意不使用StringIncrement(其对数值已经实现了锁);

右键项目,添加StackExchange.redis
在这里插入图片描述
在这里插入图片描述
上图中是因为我已经安装过,所以右边显示的是卸载和更多,如果未安装过则显示的是安装按钮。

class Program
{
	static ConnectionMultiplexer redis;
	static IDatabase _db;
	static RedisValue Token = Environment.MachineName;
	static void Main(string[] args)
	{
		var options = ConfigurationOptions.Parse("localhost");
		options.AllowAdmin = true;
		redis = ConnectionMultiplexer.Connect(options);
		_db = redis.GetDatabase();

		//未使用锁  2个线程同时对一个数据进行加操作
		for (var i = 0; i < 2; i++)
		{
			var key = "key";
			Task.Factory.StartNew((index) =>
			{
				for (var j = 0; j < 100; j++)
				{
					var tmp = _db.StringGet(key);
					var data = 0;
					int.TryParse(tmp, out data);
					_db.StringSet(key, (data + 1).ToString(), TimeSpan.FromSeconds(5));
				}
			Console.WriteLine("[未锁]-线程" + (Convert.ToInt32(index) + 1) + "值为:" + _db.StringGet(key));
		}, i);
	}

	//使用锁
	for (var i = 0; i < 2; i++)
	{
		Task.Factory.StartNew((index) =>
		{
			var key = "key2";
			for (var j = 0; j < 100; j++)
			{
				while (true)
				{
					if (StringLockToUpdate(key))
					break;
				}
			}
			Console.WriteLine("[StackExchange锁]-线程" + (Convert.ToInt32(index) + 1) + "值为:" + _db.StringGet(key));
		}, i);
	}
	
	//使用代码级锁
	for (var i = 0; i < 2; i++)
	{
		var key = "key3";
		Task.Factory.StartNew((index) =>
		{
			for (var k = 0; k < 100; k++)
			{
				StringLockToUpdateByNormalLock(key);
			}
			Console.WriteLine("[代码级锁]-线程" + (Convert.ToInt32(index) + 1) + "值为:" + _db.StringGet(key));
		}, i);
	}
	Console.WriteLine("完成");
	Console.ReadKey();
}

	/// <summary>
	/// StackExchange.redis锁
	/// </summary>
	/// <param name="key">数据Key</param>
	/// <returns></returns>
	static bool StringLockToUpdate(string key)
	{
		var flag = false;
		//设置timespan避免死锁
		if (_db.LockTake("LockKey", Token, TimeSpan.FromSeconds(5)))
		{
			try
			{
				var tmp = _db.StringGet(key);
				var data = 0;
				int.TryParse(tmp, out data);
				_db.StringSet(key, data + 1, TimeSpan.FromSeconds(5));
				flag = true;
			}
			catch (Exception)
			{
				//var a = ex.Message + "\r\n" + ex.StackTrace;
				 var b = string.Empty;
			}
			finally
			{
				_db.LockRelease("LockKey", Token);
			}
		}
		return flag;
	}


	static object myLock = new object();
	/// <summary>
	/// 代码级锁实现锁
	/// </summary>
	/// <param name="key">数据key</param>
	static void StringLockToUpdateByNormalLock(string key)
	{
		lock (myLock)
		{
			var tmp = _db.StringGet(key);
			var data = 0;
			int.TryParse(tmp, out data);
			_db.StringSet(key, data + 1, TimeSpan.FromSeconds(5));
		}
	}
}

执行结果如图:
执行结果
未使用锁的情况下,两个线程同时对同一个redis值进行变更,最终值无法按照我们预期保证数据准确。
代码级锁和StackExchange锁都实现了我们预期的效果;
根据业务需要,使用StackExchange锁或代码级锁均可实现锁效果。

上述效果中,StackExchange锁和代码级锁都实现了同样的效果
StackExchange是通过不断重试(While)来实现每一次每一次循环的操作都有效执行;
代码级锁中使用了Lock,他的实现是类似队列的;
所以从实现上述业务效果的性能上来看,代码级锁应该优于不断重试的方式;

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值