1、redis下载安装
Github下载地址:https://github.com/MicrosoftArchive/redis/releases
安装过程不做写明
1、VS引用StackExchange.Redis
通过“工具”=》“库程序包管理器”=》“程序包管理器控制台”
pm>Install-Package StackExchange.Redis -Version 1.2.5
也可通过vs中NuGet获取--(不建议此方式,因为你所用的.net freamwork版本不一定一直,可用过https://www.nuget.org/packages/StackExchange.Redis这个redis官网查看所用freamwork版本对应的redis版本,用控制台添加引用)
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication5
{
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();
}
///
/// StackExchange.redis锁
///
/// 数据Key
///
static bool StringLockToUpdate(string key)
{
var flag = false;
//设置timespan避免死锁,“LockKey”为锁的名字,共同操作时此处唯一
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 b = string.Empty;
}
finally
{
_db.LockRelease("LockKey", Token);
}
}
return flag;
}
static object myLock = new object();
///
/// 代码级锁实现锁
///
/// 数据key
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,他的实现是类似队列的;
所以从实现上述业务效果的性能上来看,代码级锁应该优于不断重试的方式;