是一个';ConcurrentQueue';处理所需流程的最佳方法是';它类似于';银行账户交易&x27;用C#编写代码?

是一个';ConcurrentQueue';处理所需流程的最佳方法是';它类似于';银行账户交易&x27;用C#编写代码?,c#,rest,asp.net-web-api,odata,C#,Rest,Asp.net Web Api,Odata,总的来说,我正在尝试制定类似于银行如何处理其账户交易的代码。例如,两个从同一账户借记款项的请求永远不能同时执行,因为这可能导致账户透支 关键任务的快速总结包括以下内容: 这段代码是按照OData标准写入RESTful api的,因此需要为每个请求提供一个响应,并提供一个指向新创建资源的链接 不能同时处理对同一资源的两个请求 根据我到目前为止所做的研究,由于它的线程安全性,似乎ConcurrentQueue是一种可行的方法,但是,我不知道如何为每个包含新资源链接的请求提供IHttpActionRe

总的来说,我正在尝试制定类似于银行如何处理其账户交易的代码。例如,两个从同一账户借记款项的请求永远不能同时执行,因为这可能导致账户透支

关键任务的快速总结包括以下内容:

  • 这段代码是按照OData标准写入RESTful api的,因此需要为每个请求提供一个响应,并提供一个指向新创建资源的链接
  • 不能同时处理对同一资源的两个请求
  • 根据我到目前为止所做的研究,由于它的线程安全性,似乎ConcurrentQueue是一种可行的方法,但是,我不知道如何为每个包含新资源链接的请求提供IHttpActionResult响应,因为似乎唯一会得到响应的请求是队列中的最后一个请求

    下面是我到目前为止的代码。任何帮助都将不胜感激

    private static readonly ConcurrentQueue<MarketUnitAdjustmentModel> _createMarketUnitAdjustmentQueue = new ConcurrentQueue<MarketUnitAdjustmentModel>();
    
    public ServiceResponseModel CreateMarketUnitAdjustment(MarketUnitAdjustmentModel marketUnitAdjustment)
        {
            var serviceResponseModel = new ServiceResponseModel();
    
            _createMarketUnitAdjustmentQueue.Enqueue(marketUnitAdjustment);
            MarketUnitAdjustmentModel nextEntryInQueue;
            while (_createMarketUnitAdjustmentQueue.TryDequeue(out nextEntryInQueue))
            {
                serviceResponseModel = AddMarketUnitAdjustment(nextEntryInQueue);
            };
    
            return serviceResponseModel;
        }
    
    ServiceResponseModel如下所示:

        [HttpPost]
        [EnableQuery]
        [SwaggerResponse(HttpStatusCode.OK)]
        [SwaggerResponse(HttpStatusCode.Created)]
        [SwaggerResponse(HttpStatusCode.BadRequest)]
        [SwaggerResponse(HttpStatusCode.InternalServerError)]
        public IHttpActionResult Post (MarketUnitAdjustmentModel key)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest();
                }
                var serviceResponseModel = _marketUnitAdjustmentService.CreateMarketUnitAdjustment(key);
                if (!serviceResponseModel.IsSuccessful)
                {
                    //This response needs to be updated to a BadRequest or something similar
                    return Ok(serviceResponseModel.Message);
                }
                else
                {
                    // HTTP Status code of Created returns location of newly created person and the ID of that person.
                    var location = Request.RequestUri + "(" + serviceResponseModel.EntityId + ")";
                    return Created(location, serviceResponseModel.EntityId);
                }
            }
            catch(Exception)
            {
                return InternalServerError();
            }
        }
    
    public class ServiceResponseModel
    {
        public bool IsSuccessful { get; set; }
        public string Message { get; set; }
        public int? EntityId { get; set; }
    }
    
    例如,两个从同一账户借记款项的请求永远不能同时执行,因为这可能导致账户透支

    银行账户超时透支,所以你的前提有缺陷

    总的来说,我正在尝试制定类似于银行如何处理其账户交易的代码

    银行交易遵循一种可能被描述为。当您请求资金移动时,将创建一个。实际的资金流动直到一天结束时才发生,需要一种分布式的调节

    在实践中,实际的资金流动充满了怪癖和异常,因为由于所有(非常旧的)系统的差异,它无法完全自动化。因此,还必须有能力。此外,资金通常存放在一家银行,以确保在所有怪癖解决之前,资金不会交给任何一方。这还有一个好处,就是当钱存在他们的账户中时,给银行利息

    还值得注意的是,银行交易不会排队、处理和丢弃。一旦它们被创造出来,它们就永远存在(部分是出于目的)。要“删除”一笔交易,您实际上创建了第二笔抵销交易

    你是真的在尝试建立一个银行系统的模型,还是仅仅用它来做类比?因为实现队列可能不是正确的方法。相反,您应该使用工作流数据结构、复式记帐和间隙存储库结构


    我猜你的问题有一个相似之处。

    虽然John Wu的回答着眼于现实世界的银行应用,但我更理解你的问题是一个类比

    银行转账的主要问题是“独家获取资源”。启动交易时,以独占方式访问要从中转账的帐户,检查它是否满足可以完成交易的条件,以独占方式访问其他帐户,检查它是否满足条件,执行交易,释放资源


    独占访问的机制,例如监视器/锁、信号灯等。确保您不会遇到死锁

    当您实际使用多个线程时,像ConcurrentQueue这样的线程安全集合非常有用。在您的情况下,这两个借记货币事务将是两个不同的请求,而不是在一个请求中生成两个任务/线程。那么,在它的两个不同请求中,不是每个请求都要实例化一个新的ConcurrentQueue吗?可能您正在寻找数据库事务,这些事务在使用时会临时锁定资源,以便不对其执行并行查询。更新--刚刚注意到您有一个静态列表,因此不会像我上面提到的那样为每个请求创建新列表。但是,即使不使用任何队列,执行情况又会有多大的不同呢?您只需退出队列并执行
    AddMarketUnitAdjustment
    方法,该方法与不使用队列时发生的情况大致相同。还是我遗漏了什么?@John Wu和Bernhard-我感谢你的两个回复。我对多线程的概念还不熟悉,自从发布我的问题以来,我了解到我们的代码不使用单例结构,所以我想我的问题解决方案实际上不会解决这个问题。听起来,存储过程可能是防止非法事务(即不符合逻辑要求的事务)的最佳方法,但我们认为,由于它的使用量很小,所以这个问题可以忽略不计。