“首先这是一篇快餐文,听说现在年轻人就喜欢快餐,不知道是真是假
稍微写过程序的同学都知道并发中最难的就是资源的竞争问题,说的白话一点,其实是数据一致性的问题。最常见的莫过于数据库的Insert和Update操作,对于同一条数据的多个update操作,其实DB在内部利用锁机制把请求顺序化了,换句话说,数据库已经帮你控制好了并发,使应用程序不会出现相互覆盖的操作。举个简单例子,假如用户账号里有100元钱,这个时候有两条update语句
update table set 余额=余额-10 where userId=100
update table set 余额=余额-20 where userId=100
无论如何并发执行两条sql,你会发现结果总是正确的,用于的余额总是70。如果按照对于资源竞争的理解,如果没有控制机制,总会有几率出现余额为90或者80的情况。这就是数据库的魅力,关系型数据库在一致性ACID这方面做的很好,这也是我提倡用DB事物代替分布式事务,少用分布式事务的原因之一。另外以前写过一篇关于数据库悲观锁和乐观锁的文章,感兴趣的同学可以去赐教:
言归正传,在DB的上层应用中,也避免不了对某些资源的并发修改,而大部分编程语言也提供了锁机制,比如c#的Lock语法,java中的synchronized 等。然而今天的话题确实另外一码事,居然说了这么一大堆废话,意不意外?
请看一下程序,我相信很多同学也写过
static object ObjLock = new object();
public static void Run(int Id)
{
//开始处理的程序100行
lock (ObjLock)
{
//业务处理程序1000 行
}
//结尾程序100 行
}
image
如果并发比较大的话,你的老板又要跳脚杀一个程序员祭天了。以上代码有没有问题呢?当然有,要不然我发它干嘛
站在业务功能的角度来说,以上代码实现所需业务,而且进行资源竞争问题的处理没有问题,加入lock方法体中的代码执行时间过长,在稍微有一些并发的情况下,程序会非常慢。原因在于lock这里。
在编写并发的代码中,我们要时刻记住,我们要控制的是对同一个资源的并发请求做控制,对应以上代码,不难发现,其实相当于对所有的资源竞争做了顺序化,这样是得不偿失的。
怎么修改呢?我们可以对每个资源实现一个lock
static ConcurrentDictionary<string,object> LockDic = new System.Collections.Concurrent.ConcurrentDictionary<string, object>();
public static void Run(int Id)
{
//根据要控制的资源id,进行分别控制
var lockObj= LockDic.GetOrAdd("资源的标示",(k)=>{ return new object(); });
//开始处理的程序100行
lock (lockObj)
{
//业务处理程序1000 行
}
//结尾程序100 行
}
每个竞争资源一个lock,这样就能实现多个资源的并行处理,你们说对不对?