.Net Core 使用AutoRestEvent实现锁定订单功能

多人点餐小程序,同一桌的顾客可以看见彼此所点的菜品,某一顾客点击提交订单过程中不允许其他顾客进行点餐操作,防止订单提交前后点餐数据不一致。即提交订单的过程中锁定订单,顾客确认或取消订单后解除锁定。

一、AutoResetEvent的基本使用

微软官方文档地址:

AutoResetEvent Class (System.Threading) | Microsoft Learnicon-default.png?t=N7T8https://learn.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent?view=net-6.0

1、AutoResetEvent的构造方法

public AutoResetEvent (bool initialState);

构造方法参数 initialState

true:创建实例对象,并设置对象的信号量为true。

false:创建实例对象,并设置对象的信号量为false。

AutoResetEvent autoResetEvent = new AutoResetEvent(true);

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

2、AutoRessetEvent的方法

(1)WaitOne()

        监听autoResetEvent对象的信号量,若信号量的值为true,WaitOne()方法返回true,线程继续执行;若autoResetEvent对象的信号量为false,WaitOne()方法不返回,等待信号量变为true,等待过程中阻塞当前线程。

(2)WaitOne(int milliseconds)

        在指定的毫秒内监听autoResetEvent对象的信号量,指定时间内autoResetEvent对象的信号量为变为true,WaitOne方法返回true,线程继续执行。若指定时间内等待autoResetEvent对象的信号量没有变为true,WaitOne方法返回false,线程不再监听信号量,不会阻塞。

(3)Set()

        将autoResetEvent对象的信号量变为true。

(4)Reset()

        将autoResetEvent对象的信号量变为false。

3、对AutoResetEvent的理解

(1)AutoResetEvent的实例对象拥有一个信号量属性,该属性相当于一个红绿灯。信号量==true时绿灯亮起,信号量==false时红灯亮起。线程调用WaitOne()方法监听红绿灯,红等亮起时所有线程阻塞;绿灯亮起时只有一条线程能通过,该线程通过后AutoResetEvent会自动调用Reset方法将信号量变为false即亮起红灯,剩下的线程阻塞。

(2)线程之间监听同一个红绿灯的线程是竞争关系。即调用同一个AutoResetEvent实例对象的WaitOne()方法的线程是竞争关系。

(3)线程之间监听不同红绿灯的线程不是竞争关系,即调用不同AutoResetEvent实例对象的WaitOne()方法的线程不是竞争关系。

4、AutoResetEvent案例

(1)构造方法为参数为true时,创建对象并将信号量设为true。第1个WaitOne()方法返回true;

第2个WaitOne()方法返回false。

//创建实例对象并将信号量设为true
AutoResetEvent autoResetEvent = new AutoResetEvent(true);

bool isGreenLight1 = autoResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight1);

//第1个WaitOne方法执行成功后,autoResetEvent自动调用Reset()方法将信号量改为false

bool isGreenLight2 = autoResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight2);
/*
结果:
    isGreenLight1 == True;
    isGreenLight2 == False;
*/


(2)构造方法为参数为false时,创建对象并将信号量设为false,所有的WaitOne()方法都返回false。

//创建实例对象并将信号量设为false
AutoResetEvent autoResetEvent = new AutoResetEvent(false);

bool isGreenLight1 = autoResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight1);

bool isGreenLight2 = autoResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight2);

/*
结果:
    isGreenLight1 == False;
    isGreenLight2 == False;
     
*/

(3)构造方法为参数为false时,创建对象并将信号量设为false。调用Set()方法将信号量改为true,第1个WaitOne()方法返回true。

//创建实例对象并将信号量设为true
AutoResetEvent autoResetEvent = new AutoResetEvent(false);

autoResetEvent.Set();//将信号量改为true

bool isGreenLight1 = autoResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight2);

//第1个WaitOne方法执行成功后,autoResetEvent自动调用Reset()方法将信号量改为false

bool isGreenLight2 = autoResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight2);

/*
结果:
    isGreenLight1 == True;
    isGreenLight2 == False;
*/

二、本项目中使用AutoResetEvent

1、在program.cs文件中注册一个单例的字典,单例保证所有客户端访问到同一个字典,字典的key为string类型的餐桌编号,value为AutoResetEvent对象。此数据结构保证同一餐桌的顾客调用同一个AutoResetEvent实例,不同餐桌的顾客互不影响。

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

builder.Services.AddSingleton<Dictionary<string,AutoResetEvent>>();

2、在Controller中依赖注入该字典。

public class WX_OrderController : ControllerBase
{
    private Dictionary<string, AutoResetEvent> autoResetEventDic;

    public WX_OrderController(Dictionary<string, AutoResetEvent> autoResetEventDic)
    {
        this.autoResetEventDic = autoResetEventDic;
    }
}

3、点餐-添加菜品 接口如下:客户端发起的第一条点餐请求将会创建AutoResetEvent实例对象,并将该对象添加入字典中,以餐桌编号为key。实例对象autoResetEvent的信号初始值为true,当前线程调用WaitOne方法返回true,线程继续执行;其余线程必须等待。防止多顾客点餐导致订单数据不一致。

注意:接口retrun之前需要调用autoResetEvent的Set()方法,将信号量改为true,否则会导致订单锁定。

public IActionResult AddOrderDetail(WXOrderDetailVo wXOrderDetailVo)
{
     AutoResetEvent? autoResetEvent;

     if (!autoResetEventDic.TryGetValue(wXOrderDetailVo.TableNo, out autoResetEvent)){

         autoResetEventDic.Add(wXOrderDetailVo.TableNo,new AutoResetEvent(true));
     }
     bool isGreenLight = autoResetEventDic[tableNo].WaitOne(TimeSpan.FromSeconds(1));
     if (!isGreenLight ){
         return BadRequest("订单提交中,请勿点餐");
     }
     else
     {
        /*执行添加菜品操作*/

        autoResetEventDic[tableNo].Set();
     }
     retrun Ok("菜品添加成功");
}

备注:传入参数wXOrderDetailVo包含了餐桌信息—TableNo

4、客户端点击 ”现在下单“ 按钮 发送锁定订单请求,该接口调用对应餐桌的AutoRestEvent对象的WaitOne()方法,WaitOne()方法成功调用后AutoRestEvent对象会自动调用Reset()方法将信号量改为false,此时订单锁定无法再点餐。

[HttpGet]
public IActionResult LockOrder(string tableNo)
{
    if (string.IsNullOrEmpty(tableNo)) return BadRequest("tableNo不能为空");
    AutoResetEvent? autoResetEvent;
    if (!autoResetEventDic.TryGetValue(tableNo, out autoResetEvent))
    {
        autoResetEventDic.Add(tableNo, new AutoResetEvent(true));
    }
    autoResetEventDic[tableNo].WaitOne();
    return Ok("订单锁定中");
}

5、客户端确定或取消提交订单都会调用UnLockOrder接口,将对应餐桌的AutoRestEvent对象的信号量改为true,此时订单解除锁定可以继续点餐。

[HttpGet]
public IActionResult UnLockOrder(string tableNo)
{
    AutoResetEvent? autoResetEvent;
    if (!autoResetEventDic.TryGetValue(tableNo, out autoResetEvent))
    return Ok($"{tableNo}订单未锁定,不需要解锁");

    bool UnLockResult = autoResetEvent.Set();
    if (!UnLockResult) return BadRequest($"解除锁定{tableNo}的订单失败");

    return Ok($"Unlock {tableNo}订单成功");
}

三、补充说明ManulRestEvent

用法与AutoManulEvent类似,但调用Waitone()方法后不会自动调用Reset()方法。

//创建实例对象并将信号量设为false
ManualResetEvent manualResetEvent = new ManualResetEvent(false);

manualResetEvent.Set();

//调用Set()方法后将信号量改为true,后续的WaitOne()方法监听到的信号量一直为true

bool isGreenLight1 = manualResetEvent.WaitOne(1000);//1秒内信号量是否为true

Console.WriteLine(isGreenLight1);

bool isGreenLight2 = manualResetEvent.WaitOne(500);//0.5秒内信号量是否为true

Console.WriteLine(isGreenLight2);

/*
结果:
    isGreenLight1 == True;
    isGreenLight2 == True;
*/
  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值