c# 并发编程系列之六:常见的几种锁及各自的使用场景-Monitor锁

时间:2021-08-16
本文章向大家介绍c# 并发编程系列之六:常见的几种锁及各自的使用场景-Monitor锁,主要包括c# 并发编程系列之六:常见的几种锁及各自的使用场景-Monitor锁使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

锁的核心作用是用来控制并发环境下对变量和资源的有序访问,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; // 定义一个实例变量,模拟记录商品的销量;

    
public void OnGet() { } public void OnPost() { Parallel.For(0, userCount, i => SaveOrder(i)); // 使用 Parallel.For() 模拟10000个用户并发执行下单操作。 //并发循环完后跳转到完成页面,并将用户数和购买的商品数在 Finish.cshtml 页面显示出来。 string url = string.Format("/Finish?usercount={0}&salecount={1}", userCount, saleCount); Response.Redirect(url); } public void SaveOrder(int i) {
       // do something saleCount
= saleCount + 1; // 用户下单完成后将商品销量 +1 } }

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 份,即使多次执行其数量也小于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);
            }
        }    

编译后刷新页面,然后再次点击按钮,得到结果如下:

和我们期望的结果一样。

接下来我们分析一下上述意外情况产生的原因及另外几种改进写法的不同之处。

(未完待续...)

作者:屏风马
本文版权归作者和博客园共有,转载请注明原文链接和出处。

原文地址:https://www.cnblogs.com/pfm33/p/14980855.html