悲观锁、乐观锁用来处理并发情况下出现的问题
悲观锁:
悲观的认为每次去拿数据的时候都会被别人修改,所以每次在拿数据的时候都会“上锁”,操作完成之后再“解锁”。 在数据加锁期间,其他人(其他线程)如果来拿数据就会等待,直到去掉锁。
实现原理:开启事务,利用排它锁和行锁将该条数据锁住,其他线程如果要访问,必须得该线程提交完事务,锁释放后才能使用
乐观锁:
乐观的认为该条数据不会被占用,自己先占了再说,占完了之后再看看是不是占上了,如果没占上就是操作失败,提示给用户。
实现原理:添加一个rowversion字段,凡是对该条数据进行过update操作,rowversion字段的值都会发生变化。在update时判断rowversion是不是和查询出来的一致,如果不一致就提示用户失败
两种锁进行对比:
悲观锁使用的体验更好,但是对系统性能的影响大,只适合并发量不大的场合。 乐观锁适用于“写少读多”的情况下,加大了系统的整个吞吐量,但是“乐观锁可能失败”给用户的体验很不好。
悲观锁案例:
{ Console.WriteLine("司机您好,请输入您的名字"); string driverName = Console.ReadLine(); string connstr = ConfigurationManager.ConnectionStrings["connstr"].ConnectionString; using (SqlConnection conn = new SqlConnection(connstr)) { conn.Open(); using (var tx = conn.BeginTransaction()) { try { Console.WriteLine("开始查询"); using (var selectCmd = conn.CreateCommand()) { selectCmd.Transaction = tx; //排它锁和行锁,针对访问线程锁住该行,不能继续往下执行,只有事务提交完,其他线程才能访问 selectCmd.CommandText = "select * from OrderInfor with(xlock,ROWLOCK) where id=1"; using (var reader = selectCmd.ExecuteReader()) { if (!reader.Read()) { Console.WriteLine("没有id为1的订单"); return; } string dName = null; string isRobbed = null; if (!reader.IsDBNull(reader.GetOrdinal("driverName"))) { dName = reader.GetString(reader.GetOrdinal("driverName")); } if (!reader.IsDBNull(reader.GetOrdinal("isRobbed"))) { isRobbed = reader.GetString(reader.GetOrdinal("isRobbed")); } //表示该订单已经被抢了 if (isRobbed == "1" && !string.IsNullOrEmpty(dName)) { if (driverName == dName) { Console.WriteLine("该订单早已经被我抢了"); } else { Console.WriteLine($"该订单早已经被司机【{dName}】抢了"); } //不再往下执行 Console.ReadKey(); return; } } Console.WriteLine("查询完成,开始执行update操作"); using (var updateCmd = conn.CreateCommand()) { updateCmd.Transaction = tx; updateCmd.CommandText = "Update OrderInfor set driverName=@driverName,isRobbed=@isRobbed where id=1"; updateCmd.Parameters.Add(new SqlParameter("@driverName", driverName)); updateCmd.Parameters.Add(new SqlParameter("@isRobbed", "1")); updateCmd.ExecuteNonQuery(); } Console.WriteLine("结束update操作"); Console.WriteLine("按任意键进行事务提交"); Console.ReadKey(); } tx.Commit(); Console.WriteLine("事务提交成功"); } catch (Exception ex) { Console.WriteLine(ex); tx.Rollback(); } } } }
乐观锁案例:
{ try { Console.WriteLine("司机您好,请输入您的名字"); string driverName = Console.ReadLine(); using (LockDemoDBEntities1 ctx = new LockDemoDBEntities1()) { Console.WriteLine("开始查询"); //一定要遍历一下 SqlQuery 的返回值才会真正执行 SQL var orderInfor = ctx.Database.SqlQuery<OrderInfor2>("select * from OrderInfor2 where id=1").Single(); //表示该订单已经被抢了 if (orderInfor.isRobbed == "1" && !string.IsNullOrEmpty(orderInfor.driverName)) { if (driverName == orderInfor.driverName) { Console.WriteLine("该订单早已经被我抢了"); } else { Console.WriteLine($"该订单早已经被司机【{orderInfor.driverName}】抢了"); } //不在往下执行 Console.ReadKey(); return; } Console.WriteLine("查询完成,按任意键进行抢单"); Console.ReadKey(); Console.WriteLine("正在抢单中。。。。。"); //休眠3s,模拟高并发抢单 Thread.Sleep(3000); int affectRows = ctx.Database.ExecuteSqlCommand("Update OrderInfor2 set driverName={0},isRobbed={1} where id=1 and rowversion={2}", driverName, "1", orderInfor.rowversion); if (affectRows == 0) { Console.WriteLine("抢单失败"); } else if (affectRows == 1) { Console.WriteLine("抢单成功"); } else { Console.WriteLine("见鬼了"); } } } catch (Exception ex) { Console.WriteLine("失败了"); Console.WriteLine(ex.Message); throw; } }