为什么我的锁没有阻塞?

时间:2014-03-03 15:43:33

标签: c# multithreading

以下代码似乎没有锁定。

以下是我正在尝试做的一些背景知识:

我有一个库存管理系统,可以执行以下操作:

  1. 收到订单
  2. 分配现有库存以履行订单
  3. 如果无法填写订单,则将订单标记为延期交货
  4. 尝试在添加新广告资源时填写延期交货
  5. 允许取消订单。
  6. 因此,假设有100个订单延期交货,然后出现一批库存,可以填满所有100个延期交货。当我的Warehousing类(未在下面显示)收到货件时,将调用下面定义的AllocateOutstandingRequests方法。最终我们在下面的AllocateInventory方法的第一个foreach循环中结束,它通过100个订单进行迭代,尝试为每个订单分配库存。现在,假设我的Messaging类(下面部分定义)收到该列表的第2号订单的CancelOrder时,循环在第80项。 CancelOrder例程在Allocation类中调用我的AddCancel例程,并且应该在锁定时阻止(_cancelsLock),但事实并非如此。我用时间戳和threadId遍历这段代码,我甚至在循环中设置了3秒的延迟,试图为延期交货的项目分配库存。我可以在跟踪中看到循环完全按预期工作,每次分配之间有3秒,但我也可以看到AddCancel调用立即返回到我的Messaging类,没有延迟;我预计会延迟,它应该阻止,直到所有100个订单都已分配并锁定被释放。我究竟做错了什么?似乎AddCancel中的锁没有做任何事情!

    我在代码中添加了日志记录的样子,并在日志文件中添加了实际的日志条目。可以看出,Messaging.ReceiveInventory [threadId 38]接收库存并调用Allocation.AllocateOutstandingRequests,后者又启动任务[threadId 26]:

    2014-02-28 17:00:08,871 [38] INFO - 用户处理传入的ReceiveDrugs请求:WELLDYNERX \ privera

    2014-02-28 17:00:08,871 [38] INFO - NDC 00002323230已添加到申请人110的库存中

    2014-02-28 17:00:08,871 [26] INFO - ......分配未完成的请求

    2014-02-28 17:00:08,887 [26]信息 - 试图分配100个未完成的请求

    2014-02-28 17:00:08,934 [26] INFO - 延期交货的RequestUID 100689已分配

    2014-02-28 17:00:23,934 [26] INFO - 延期交货的RequestUID 100690已分配

    2014-02-28 17:00:25,293 [42]信息 - 处理收到的取消通知; UID:100689,来自PRIVERA for RequestorUID:110

    2014-02-28 17:00:25,309 [42] INFO - UID的取消通知:100689,已处理。

    2014-02-28 17:00:39,012 [26] INFO - 延期交货的RequestUID 100691已分配

    2014-02-28 17:00:54,012 [26] INFO - 延期交货的RequestUID 100692已分配

    分配类:

    public static class Allocation
    {
        public static void AllocateOutstandingRequests()
        {
            var factory = new TaskFactory(_orderedTaskScheduler);
            TaskScheduler.UnobservedTaskException += OrderedTaskScheduler_UnobservedTaskException;
    
            factory.StartNew(() =>
            {
                Trace.TraceInformation("...Allocating outstanding requests");
                List<QueueingRequest> backorderedRequests = InventoryDao.GetBackorderedRequests();
                List<AllocationRequest> backorderedRequestsAllocated =
                    AllocateInventory(backorderedRequests.OrderBy(r => r.RequestUID).ToList());
                SendAllocationResponses(backorderedRequestsAllocated);
                Trace.TraceInformation("Completed allocating outstanding requests...");
            });  
        }
    
        static List<AllocationRequest> AllocateInventory(List<QueueingRequest> outstandingRequests)
        {
            List<AllocationRequest> allocatedBackorderedRequests = new List<AllocationRequest>();
    
            lock (_cancelsLock)
            {
                Trace.TraceInformation(string.Format("Attempting to allocate {0} outstanding requests", outstandingRequests.Count));
    
                foreach (QueueingRequest queuedRequest in outstandingRequests)
                {
                    if (_cancels.Contains(queuedRequest.RequestUID)) continue;
    
                    AllocationRequest allocationRequest = new AllocationRequest(queuedRequest);
                    if (AllocateOrder(allocationRequest))
                    {
                        Trace.TraceInformation(string.Format("Backordered RequestUID {0} Allocated", queuedRequest.RequestUID));
                        allocatedBackorderedRequests.Add(allocationRequest);
                    }
    
                    for (int iSleepAlot = 0; iSleepAlot < 5; iSleepAlot++)
                        System.Threading.Thread.Sleep(3000);
                }
    
                // Check to see if a CancelOrder came thru for backordered requests 
                // that the code above allocated inventory for.
                foreach (int requestUID in allocatedBackorderedRequests.Select(r => r.RequestUID))
                {
                    if (_cancels.Contains(requestUID))
                        _cancels.Remove(requestUID);
                }
            }
    
            return allocatedBackorderedRequests;
        }
    
        static bool AllocateOrder(AllocationRequest request)
        {
            bool inventoryAllocated = false;
    
            try
            {
                if (InventoryDao.SaveAllocation(request))
                    inventoryAllocated = Warehousing.AllocateDrugs(request.RequestorUID, request.Items);
            }
            catch (RequestAlreadyAllocatedException ex)
            {
                inventoryAllocated = true;
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
                throw;
            }
    
            return inventoryAllocated;
        }
    
        public static bool AddCancel(int requestUID)
        {
            bool requestStatusChangedToAllocated = false;
            _cancels.Add(requestUID);
    
            // block until backordered requests are allocated.
            lock (_cancelsLock)
            {
                requestStatusChangedToAllocated = !_cancels.Contains(requestUID);
    
                if (!requestStatusChangedToAllocated)
                    _cancels.Remove(requestUID);
            }
    
            return requestStatusChangedToAllocated;
        }
    
        static readonly TaskScheduler _orderedTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(1);
        static readonly List<int> _cancels = new List<int>();
        static readonly object _cancelsLock = new object();
    }
    

    消息传递类:

    public static class Messaging
    {
        public static void CancelOrder(CancelNotification notification)
        {
            Trace.TraceInformation(string.Format("Processing incoming CancelNotification; UID:{0}, from {1} for RequestorUID:{2}",
                                                     notification.RequestUID,
                                                     notification.User,
                                                     notification.RequestorUID));
            // This is a blocking call which returns after all backordered requests are processed.
            // The call may change the status from backordered to allocated, in which case, we'll
            // have to DeAllocateDrugs in the services cache
            bool requestStatusChangedToAllocated = Allocation.AddCancel(notification.RequestUID);
    
            // do some work
    
            Trace.TraceInformation(string.Format("CancelNotification for UID:{0}, processed.", notification.RequestUID));
        }
    
        public static List<string> ReceiveInventory(List<ReceivedInventory> received, string user, string comment)
        {
            Trace.TraceInformation(string.Format("Processing incoming ReceiveDrugs request by User:{0}", user));
    
            foreach (ReceivedInventory inventory in received)
            {
                // do some work
                Trace.TraceInformation(string.Format("NDC {0} added to inventory for requestor {1}", drugInventory.NDC, inventory.RequestorUID));
            }
    
            // re-evaluate allocations after inventory is loaded
            Allocation.AllocateOutstandingRequests();
        }
    }
    

1 个答案:

答案 0 :(得分:0)

锁定会阻止。我的单元测试使用了错误的数据。很抱歉浪费任何人的时间。