多线程编程系列学习之---lock关键字
概念
lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。
lock 只适用单机情况,分布式和负债均衡可以考虑分布式锁。
形式
- lock(this)
- lock(string)
- lock(object)
- lock(staticObject)
测试代码:
class Program
{
const string firstOrderId = "001";
const string secondOrderId = "002";
const string thirdOrderId = "003";
static void Main()
{
test(LockType.LockThis);
//test(LockType.LockString);
//test(LockType.LockObject);
//test(LockType.LockStaticObject);
Console.ReadLine();
}
static void test(LockType lockType)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("------------测试相同订单------------");
Console.ForegroundColor = ConsoleColor.White;
OrderPay(firstOrderId, 1, lockType);
OrderPay(firstOrderId, 2, lockType);
OrderPay(firstOrderId, 3, lockType);
Thread.Sleep(10000);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("------------测试不同订单------------");
Console.ForegroundColor = ConsoleColor.White;
OrderPay(firstOrderId, 1, lockType);
OrderPay(secondOrderId, 1, lockType);
OrderPay(thirdOrderId, 1, lockType);
}
static void OrderPay(string orderId, int threadNo, LockType lockType)
{
new Thread(() => new Payment(orderId, threadNo).Pay(lockType)).Start();
Thread.Sleep(10);
}
}
public class Payment
{
private readonly string LockString;
public readonly int ThreadNo;
private readonly Object LockObj = new object();
private static readonly Object StaticLockObj = new object();
public Payment(string orderID, int threadNo)
{
LockString = orderID;
ThreadNo = threadNo;
}
public void Pay(LockType lockType)
{
ShowMessage("等待锁资源");
switch (lockType)
{
case LockType.LockThis:
lock (this)
{
showAction();
}
break;
case LockType.LockString:
lock (LockString)
{
showAction();
}
break;
case LockType.LockObject:
lock (LockObj)
{
showAction();
}
break;
case LockType.LockStaticObject:
lock (StaticLockObj)
{
showAction();
}
break;
}
ShowMessage("释放锁");
}
private void showAction()
{
ShowMessage("进入锁并开始操作");
Thread.Sleep(2000);
ShowMessage("操作完成,完成时间为" + DateTime.Now);
}
private void ShowMessage(string message)
{
Console.WriteLine(String.Format("订单{0}的第{1}个线程 {2}", LockString, ThreadNo, message));
}
}
public enum LockType
{
LockThis = 0,
LockString = 1,
LockObject = 2,
LockStaticObject = 3
}
lock(this)结果:
可以看出lock(this),如果this是个普通的类非静态非单例,那么lock(this)并不满足我们的需求,甚至除了当前线程并看不出有任何作用。
lock(string) 结果:
Lock(LockString) 从结果上来看比较契合要求,但是由于CRL针对string特殊字符串驻留机制,针对相同字符串值的不同字符串变量对均指向同一个托管堆中内存块。可能会造成后期类似订单支付同样的业务拓展所带来的冲突,订单的订单ID与其他业务的标识ID字符串值相同,这样会导致不同业务间的线程互斥
lock(object) 结果:
可以通过 typeof 运算符或反射获取从而改变对象,本质和lock(this)一样
lock(staticObject) 结果:
Lock(StaticObject) 实现了对于同一笔的订单线程互斥,但是不符合的是对于不同笔的订单同样进行了互斥。
总结:当同步对共享资源的线程访问时,请锁定专用对象实例(例如,private readonly object balanceLock = new object();)或另一个不太可能被代码无关部分用作 lock 对象的实例。 避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用。 具体而言,避免将以下对象用作 lock 对象:
- this(调用方可能将其用作 lock)。
- Type 实例(可以通过 typeof 运算符或反射获取)。
- 字符串实例,包括字符串文本,(这些可能是暂存的)。