在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
锁的核心作用是用来控制并发环境下对变量和资源的有序访问,c#中常见的锁有如下几种类型: (1) Monitor (2) Mutex (3) ReaderWriterLockSlim (4) SpinLock (5) Semaphore 下面我们就来逐个看一看这些不同类型锁的使用场景和使用方式 。 前置条件: 为了使示例更具有参考性,我们照例还是建一个 ASP.NET core Razor的网站项目, 把并发放到网站环境下去运行,这样可以模拟一个多用户的使用场景。项目目录如下: 在此项目下新建 Finish.cshtml 和 Monitor.cshtml 两个文件,其中 Monitor.cshtml 用来执行并发 , 执行完后跳转到 Finish.cshtml 并将结果显示出来。 1 . 在 Shared 目录下 找到 _Layout.cshtml 文件 , 将 Monitor.cshtml 页面的链接放到菜单中去, 方便我们去点击,代码如下: <ul class="navbar-nav flex-grow-1"> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-page="/Monitor">Monitor</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a> </li> </ul> 2 . 在 Monitor.cshtml 页面添加如下的代码,其功能是在表单中放一个Submit 按钮, 我们点击后进行 POST 提交。 <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about </p> <form method="post"> <button type="submit">Monitor测试</button> </form> </div> 3 . 相应的我们在 Monitor.cshtml.cs 文件中按约定定义一个 OnPost( ) 方法来响应表单的 POST 操作,代码如下: public class MonitorModel : PageModel { private int userCount = 10000; //模拟10000个用户同时执行 Submit 操作; private int saleCount = 0; // 定义一个实例变量,模拟记录商品的销量; 4. 在 Finish.cshtml 页面接收 OnPost() 方法中传递过来的值然后显示出来,代码如下: <div>共有 <span style="font-size:18px;">@HttpContext.Request.Query["usercount"]</span> 人购买</div> <br /> <br /> <div>共卖出 <span style="font-size:18px;">@HttpContext.Request.Query["salecount"]</span> 份</div> 至此,我们就完成了一个模拟10000个用户同时在网站下单并完成的操作。 5. 把项目编译之后运行网站,画面如下:
点击按钮,得到如下的结果:
可以看到,结果和我们想象的不太一样,卖出的数量不是 10000 份,只有1559份, 即使打开页面多次执行其数量也仍然远小于10000 。 6 . 现在我们用 Monitor 对象对 SaveOrder(int i) 方法中的语句加锁 ,避免出现实际销量和我们看到的数量 不一致的情况,将 SaveOrder(int i) 改造如下: private readonly object obj = new object(); public void SaveOrder(int i) { try { Monitor.Enter(obj); saleCount = saleCount + 1; } finally { Monitor.Exit(obj); } } 编译后刷新页面,然后再次点击按钮,得到结果如下:
和我们期望的结果一致。 7 . 接下来我们在上面的基础上做一下演化:在 Monitor.cshtml.cs 中增加 一个静态变量 staticCount ,看看页面运行结果有什么变化,Monitor.cshtml.cs 中代码修改如下: public class MonitorModel : PageModel { private int userCount = 10000; private int saleCount = 0; private static int staticCount = 0; //增加一个静态变量 public void OnGet() { } public void OnPost() { Parallel.For(0, userCount, i => SaveOrder(i)); // 增加一个数据传递 staticcount string url = string.Format("/Finish?usercount={0}&salecount={1}&staticcount={2}", userCount, saleCount, staticCount); Response.Redirect(url); } public void SaveOrder(int i) { saleCount = saleCount + 1; // 给静态变量做 + 1 操作 staticCount = staticCount + 1; } } 在 Finish.cshtml 文件中 接收静态变量的值,代码如下: <div>共有 <span style="font-size:18px;">@HttpContext.Request.Query["usercount"]</span> 人购买</div> <br /> <br /> <div>共卖出 <span style="font-size:18px;">@HttpContext.Request.Query["salecount"]</span> 份</div> <br /> <br /> <div>静态数量 <span style="font-size:18px;">@HttpContext.Request.Query["staticcount"]</span> 份</div> 编译后运行结果如下: 然后点 Monitor 链接 重复执行 3 次得到的结果如下表:
根据上面的结果我们可以得出如下的结论: 1. 并发产生的原因是因为有多个线程在操作同一个变量,和这个变量是实例变量还是静态变量无关。 2. 产生并发的时候实例变量因为每次都会初始化,所以其值比正常执行时候小,静态变量就不一样了, 它只初始化一次,所以会产生累加的效果,多次运行后可能比正常值大。 最后,我们可以用lock关键字对简化对 Monitor对象的使用,代码如下: private readonly object obj = new object(); public void SaveOrder(int i) { lock(obj) { saleCount = saleCount + 1; } } 编译后运行结果如下: 和使用Monitor对象的效果是一样的。
|
请发表评论